Using ASP.NET MVC 3 with Razor, what's the most effective way to add an ICollection to a Create view? - asp.net-mvc-3

I'm using Entity Framework Code First to generated my database, so I have an object defined like the following:
public class Band
{
public int Id { get; set; }
[Required(ErrorMessage = "You must enter a name of this band.")]
public string Name { get; set; }
// ...
public virtual ICollection<Genre> Genres { get; set; }
}
Now I'm looking at a create view for this and the default scaffolding isn't adding Genres to my form, which from past experience is about what I expect.
Looking online I've found Using ASP.NET MVC v2 EditorFor and DisplayFor with IEnumerable<T> Generic types which seems to come closest to what I want, but doesn't seem to make sense with Razor and possibly MVC 3, per ASP.NET MVC 3 Custom Display Template With UIHint - For Loop Required?.
At present I've added the listing of genres to the ViewBag and then loop through that listing in my create view:
#{
List<Genre> genreList = ViewBag.Genres as List<Genre>;
}
// ...
<ul>
#for (int i = 0; i < genreList.Count; i++)
{
<li><input type="checkbox" name="Genres" id="Genre#(i.ToString())" value="#genreList[i].Name" /> #Html.Label("Genre" + i.ToString(), genreList[i].Name)</li>
}
</ul>
Outside of not yet handling cases where the user has JavaScript disabled and the checkboxes need to be re-checked, and actually updating the database with this information, it does output the genres as I'd like.
But this doesn't feel right, based on how good MVC 3 has become.
So what's the most effective way to handle this in MVC 3?

I don't send lists into my View via the ViewBag, instead I use my viewmodel to do this. For instance, I did something like this:
I have an EditorTemplate like this:
#model IceCream.ViewModels.Toppings.ToppingsViewModel
<div>
#Html.HiddenFor(x => x.Id)
#Html.TextBoxFor(x =x> x.Name, new { #readonly="readonly"})
#Html.CheckBoxFor(x => x.IsChecked)
</div>
which I put in my Views\IceCream\EditorTemplates folder. I use this to display some html for allowing the user to "check" any particular topping.
Then in my View I've got something like this:
#HtmlEditorFor(model => model.Toppings)
and that will use that result in my EditorTemplate being used for each of the toppings in the Toppings property of my viewmodel.
And then I've got a viewmodel which, among other things, includes the Toppings collection:
public IEnumerable<ToppingsViewModel> Toppings { get; set; }
Over in my controller, among other things, I retrieve the toppings (however I do that in my case) and set my viewmodel's property to that collection of toppings. In the case of an Edit, where toppings may have been selected previously, I set the IsChecked member of the TopingsViewModel and it'll set the corresponding checkboxes to checked.
Doing it this way provided the correct model binding so that when the user checked a few toppings, the underlying items in the collection reflected those selections. Worked well for me, hope it's helpful for you.

Related

Using Html.EditorFor to create a blank for for new records

Using the EditorFor templates is a really nice feature of ASP.Net MVC 3, but is it possible to get EditorFor to render an unpopulated template to allow for creation of records?
Or is there some other way to do this?
The ways in which I am trying to do this is as follows:
#Html.EditorFor(model => model)
#Html.EditorFor(x => new List<Business.ViewModel.Affiliate.Contact>())
#Html.EditorFor(new List<Business.ViewModel.Affiliate.Contact>())
#Html.EditorFor(new Business.ViewModel.Affiliate.Contact())
The first one obviously works, however the subsequent ones (which demonstrate what I am trying to do) all fail with the following error:
Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.
The model in question is:
IEnumerable<Business.ViewModel.Affiliate.Contact>
It's the responsibility of the controller to prepare the view model that will be passed to the view. So if you need for example to initialize your view view model with 5 empty contact rows you could do this simply in your controller:
public ActionResult Index()
{
var model = new MyViewModel
{
// Add 5 empty contacts
Contacts = Enumerable.Range(1, 5).Select(x => new Contact()).ToList()
};
return View(model);
}
and in your view use the EditorFor helper as usual:
#model MyViewModel
...
#Html.EditorFor(x => x.Contacts)
This will render the corresponding editor template for each of the 5 elements we have added to the Contacts collection.
If your question doesn't involve AJAX, then I would design the ViewModel as following:
class MyList
{
public List<MyRow> Rows {get;set;}
public MyRow NewRow {get;set;}
}
Then you can easily add a blank editor bound to NewRow property. And in the controller you add the NewRow to Rows on subsequent calls.

use Razor to fill dropdown with Linq2Sql data

I'm experimenting with ASP.NET MVC3 and want to simply populate a dropdown list with data I get from a LINQ2SQL class, like so
controller (I know, Linq doesn't belong in the controller)
var allUsers = (from u in _userDataContext.Users
select u).ToList();
ViewBag.allUsers = allUsers.ToList();
return View();
view:
<select id="drop_heroes">
#foreach (var u in ViewBag.allUsers)
{
<option value="#u.pk_userid">#u.email</option>
}
</select>
That works fine, but I would like to use Razor #Html.Dropdownlist to create the same dropdown list, but can't find any info to make this work with Linq data.
I know, Linq doesn't belong in the controller
Then why are you using it in a controller? Anyway, at least it's fine that you know it.
Here's an example. As always in an ASP.NET MVC application you start by defining a view model which will represent the data that you need in the view. So in your case you need to display a dropdown so you define a list of users and a selected user id:
public class MyViewModel
{
public string SelectedUserId { get; set; }
public IEnumerable<SelectListItem> Users { get; set; }
}
then you define a controller action which will populate this view model from your repository and handle it to the view:
public ActionResult Index()
{
var model = new MyViewModel
{
Users = _userDataContext.Users.ToList().Select(x => new SelectListItem
{
Value = x.pk_userid.ToString(),
Text = x.email
})
}
return View(model);
}
and finally you will have a view which will be strongly typed to your view model and use HTML helpers to generate the dropdownlist:
#model MyViewModel
#using (Html.BeginForm())
{
#Html.DropDownListFor(x => x.SelectedUserId, Model.Users)
<button type="submit">OK</button>
}
Things to notice:
Usage of view models
Usage of a strongly typed view
Usage of strongly typed HTML helpers to generate markup such as form elements and input fields
Getting rid of weakly typed structures such as ViewBag
If you follow these simple rules you will see how much easier your life as an ASP.NET MVC developer will become.

Loading multiple controls in ASP.NET MVC

I am creating my first site in asp.net MVC and I have a very beginner question in my mind. i have seen that in controller we are returning the actionview for what we want to display in the page [Most of the example in the websites I can see they are only displaying the content in the page] . What if I have to load 3 drop down list, 2 tables , 2 radio buttons etc. What is the best practice and the correct way to load these many controls on the page?
Chris
It sounds like you are expecting to use controls like one does in ASP.Net Web Forms. However, with MVC the View consists of standard HTML. The controls you mention can just be input select and so on. There are various helper classes and methods that you can use in the view to help you render the HTML you need - In particular take a look at the Razor syntax.
I'd start with looking at a couple of examples, and it should be clearer....
Here's a good one: http://www.nerddinner.com/ (source code here http://nerddinner.codeplex.com/)
Maybe pick up a couple of books from Amazon as well.
HTH
Phil
The examples you typically see use MVC's scaffolding, which creates a very simple Controller/Actions/Views to manipulate a certain Model class. But you're free to show anything you want in your pages. Here's an example on how to show a drop down list.
First create an object that will hold all the stuff you want to display on the page:
public class GameDetailsViewModel
{
public Game Game { get; set; }
public SelectList Players { get; set; }
}
Note the SelectList. It will be used as the source for the DropDownList.
Then the Action fills in this object:
public ViewResult Details(int id)
{
GameDetailsViewModel viewModel = new GameDetailsViewModel();
viewModel.Game = db.Games.Single(g => g.ID == id);
IEnumerable<Player> players = db.Players();
viewModel.Players = new SelectList(players, "ID", "FullName");
return View(viewModel);
}
Note the overload to the View() method, that takes the object we created to package the stuff we need on the page.
Then on the View, you can use an HtmlHelper to render a DropDownList:
#using (Html.BeginForm("signup", "games", FormMethod.Post))
{
#Html.DropDownList("playerID", Model.Players, "Select...", null)
<input type="submit" value="Sign up" />
}
This is a very simple example, but you can extend it to send whatever you want to the View and then render it using plain old HTML or the handy HtmlHelpers.

Strongly Typed RadioButtonlist

I want to get some options (say payment method cash, credit card etc.) and bind these to radio buttons. I believe there is no RadioButtonList in MVC 3.
Also, once radios are bound I want to show the previously selected option to the user while editing the answer.
As always you start with a model:
public enum PaiementMethod
{
Cash,
CreditCard,
}
public class MyViewModel
{
public PaiementMethod PaiementMethod { get; set; }
}
then a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel();
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
}
and finally a view:
#model MyViewModel
#using (Html.BeginForm())
{
<label for="paiement_cash">Cash</label>
#Html.RadioButtonFor(x => x.PaiementMethod, "Cash", new { id = "paiement_cash" })
<label for="paiement_cc">Credit card</label>
#Html.RadioButtonFor(x => x.PaiementMethod, "CreditCard", new { id = "paiement_cc" })
<input type="submit" value="OK" />
}
And if you want some more generic solution which encapsulates this in a helper you may find the following answer helpful.
This is how I like to bind RadioButtonLists. The view model has a collection of my strongly typed objects. For example, maybe PaymentOptions is a code table. Along with the collection is a SelectedPaymentOptionKey (or Selected*Id if you prefix your primary keys with Id). Initially this key will just be default 0, but on postback, it will hold the value of the selected item.
public class PaymentSelectionVM
{
public ICollection<PaymentOption> PaymentOptions { get; set; }
public int SelectedPaymentOptionKey { get; set; }
}
public ViewResult PaymentSelection()
{
var paymentOptions = db.PaymentOptions.ToList();
return View(
new PaymentSelectionVM {
PaymentOptions = paymentOptions,
//This is not required, but shows how to default the selected radiobutton
//Perhaps you have a relationship between a Customer and PaymentOption already,
//SelectedPaymentOptionKey = someCustomer.LastPaymentOptionUsed.PaymentOptionKey
// or maybe just grab the first one(note this would NullReferenceException on empty collection)
//SelectedPaymentOptionKey = paymentOptions.FirstOrDefault().PaymentOptionKey
});
}
Then in the View:
#foreach (var opt in Model.PaymentOptions)
{
#*Any other HTML here that you want for displaying labels or styling*#
#Html.RadioButtonFor(m => m.SelectedPaymentOptionKey, opt.PaymentOptionKey)
}
The m.SelectedPaymentOptionKey serves two purposes. First, it groups the Radio buttons together so that the selection is mutually exclusive(I would encourage you to use something like FireBug to inspect the generated html just for your own understanding. The wonderful thing about MVC is the generated HTML is fairly basic and standard so it shouldn't be hard for you to eventually be able to predict the behavior of your views. There is very little magic going on here.). Second, it will hold the value of the selected item on postback.
And finally in the post handler we have the SelectedPaymentOptionKey available:
[HttpPost]
public ActionResult PaymentSelection(PaymentSelectionVM vm)
{
currentOrder.PaymentOption = db.PaymentOptions.Find(vm.SelectedPaymentOptionKey);
....
}
The advantage of this over using SelectListItems is you have access to more of the object's properties in the case that you are displaying a grid/table and need to display many values of the object. I also like that there are no hard coded strings being passed in the Html helpers as some other approaches have.
The disadvantage is you get radio buttons which all have the same ID, which is not really a good practice. This is easily fixed by changing to this:
#Html.RadioButtonFor(m => m.SelectedPaymentOptionKey, opt.PaymentOptionKey, new { id = "PaymentOptions_" + opt.PaymentOptionKey})
Lastly, validation is a bit quirky with most all of the radio button techniques I've seen. If I really needed it, I would wire some jquery up to populate a hidden SelectedPaymentOptionsKey whenever the radio buttons are clicked, and place the [Required] or other validation on the hidden field.
Another workaround for the validation problem
ASP.NET MVC 3 unobtrusive validation and radio buttons
This looks promising but I haven't had a chance to test it:
http://memoriesdotnet.blogspot.com/2011/11/mvc-3-radiobuttonlist-including.html
You should bind your options to SelectList in ViewModel and set Selected attribute to true for previously selected option

ASP.NET MVC 3 Data Attributes - Programmatically Set UIHint from Controller

If i have a ViewModel like this:
public class SignupViewModel
{
[Required]
[DisplayName("Email:")]
public string EmailAddress { get; set; }
}
And use EditorFor to render out the form fields:
#Html.EditorFor(model => model.EmailAddress )
It will render <input type="text">. Cool.
But in this particular scenario, i have already retrieved Email from a different source, and i wish to pre-fill the form with this data, and show a label instead of a textbox (as i don't want them to change their email - don't worry about why).
I know i can use [UIHint], but can i do that programatically from the controller?
E.g:
var model = new SignupViewModel();
model.EmailAddress = GetFromMysterySource(); // How do i set a UIHint?
What's the best way to approach this? Should i use a seperate ViewModel altogether, which could mean changing my View from being strongly-typed to being dynamic, or should i not use EditorFor, or should i use a custom editor template?
Suggestions/advise would be greatly appreciated.
You can't apply an attribute at runtime. My suggestion would be to build a bit of logic into your view to control how the view renders the data. You may need to augment your model to indicate to the view which display to choose.
#if (Model.EmailAddressIsFixed)
{
#Html.DisplayFor( m => m.EmailAddress )
#Html.HiddenFor( m => m.EmailAddress ) // only if you need it to post back
}
else
{
#Html.EditorFor( m => m.EmailAddress )
}
If you are doing this in more than one place, then a custom editor template doing the same thing would probably be in order.
#Html.EditorFor( m => m.EmailAddress,
"FixedAddressTemplate",
new { Fixed = Model.EmailAddressIsFixed } )

Resources