Dynamically Add Rows when using Editor Templates and EditorFor in MVC 3 - asp.net-mvc-3

Expanding on a previous question, I'm using EditorFor to edit a list of objects of type (let's say) Hobby.
Now, I need to implement functionality to add editors for individual Hobby objects, so that the user can add additional hobbies.
I read Steven Anderson's blog about how to do that in MVC 2. However, he's rendering the individual Hobby instances using a loop and RenderPartial, rather than using EditorFor. I understand his approach of making Ajax calls to render another partial view and inserting the view result into the DOM, but don't know how to apply the Ajax add in my case, with EditorFor.
His code:
<% foreach (var item in Model)
Html.RenderPartial("GiftEditorRow", item);
%>
My code is using EditorFor like this:
// Model is a List<Hobby>
#Html.EditorFor(model => model.AllowedSurveys)
How do I add an additional Hobby editor, given that the editor is implemented as an Editor Template rather than as a Partial View?

You could replace your editor template with a partial:
#foreach (var item in Model.AllowedSurveys)
{
Html.RenderPartial("_Hobby", item);
}
and inside the partial (~/Views/controllerName/_Hobby.cshtml):
#model Hobby
<div class="editorRow">
#using(Html.BeginCollectionItem("AllowedSurveys"))
{
#Html.EditorFor(x => x.Name)
...
}
</div>
The Html.BeginCollectionItem custom helper is used to generate names with Guids and allow for reordering and adding new items to the collection using javascript. You can download it from the updated article. The one you were reading was for ASP.NET MVC 1 and is obsolete.

Related

Can't bind model to Controller, MVC3

I have 2 Views and 1 EditorTemplate
The first view is the Cart.cshtml view and this view has a Model (CartModel)
Inside of this view there is a call to the second view using:
#Html.Partial("OrderSummary", Model.CartSummaryModel)
Inside of the OrderSummary.cshtml view I have this
#Html.EditorFor(m => m.OrderItems)
And inside the EditorTemplate (called OrderItemModel) I have
#Html.DropDownListFor(m => m.SelectedQuantity, Model.QuantityList)
The problem is when causing a post on the top most view (Cart.cshtml) the model isn't binding in the controller the "CartSummaryModel" is null. When swapping to a FormCollection there are 2 keys which are:
OrderItems[0].SelectedQuantity
OrderItems[1].SelectedQuantity
How do I bind the form collection data to the Controller's action method?
It's because of the partial:
#Html.Partial("OrderSummary", Model.CartSummaryModel)
Here you are loosing the CartSummaryModel prefix that is required in your input field names. So instead of using partials use editor templates.
Go ahead and move this OrderSummary.cshtml to EditorTemplates/OrderSummary.cshtml and then inside your view replace:
#Html.Partial("OrderSummary", Model.CartSummaryModel)
with:
#Html.EditorFor(x => x.CartSummaryModel, "OrderSummary")
and if the type of the CartSummaryModel property is OrderSummary you don't even need to specify the name of the editor template as ASP.NET MVC will find it by convention:
#Html.EditorFor(x => x.CartSummaryModel)
Now you are good to go and you will see the correct keys sent to the server:
CartSummaryModel.OrderItems[0].SelectedQuantity
CartSummaryModel.OrderItems[1].SelectedQuantity

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.

ASP.NET MVC 3 - edit items dynamically added to model collection in jquery dialog

I'm new to MVC, so I wasn't sure what the best approach would be here.
I have a view model that contains several collections like this:
public class MainViewModel{
public List<AViewModel> A { get; set; }
public List<BViewModel> B {get; set; }
...}
I'm using Steve Sanderson's approach here to dynamically add items to a collection, and it's working fine as long as the child items are editable on the main view.
The problem I'm having is returning a read only list with an edit link that will open the details to edit in a popup dialog.
Since these items may be newly added, I can't use the ID property to return a partial view from the controller. It seems like I'll have to render the editors in a hidden div like this:
<div class="AEditorRow">
#using (Html.BeginCollectionItem("A"))
{
#Html.DisplayFor(l => l.ID)
#Html.DisplayFor(l => l.Name)
#Html.DisplayFor(l => l.Code)
edit <text>|</text>
delete
<div class="ADetails" style="display: none">
#using (Html.BeginForm("EditA", "Controller"))
{<fieldset>
<legend>Location</legend>
#Html.HiddenFor(model => model.ID)
<div class="editor-label">
#Html.LabelFor(model => model.Code)
</div>
Does anyone know of a better way to do this?
After working on this issue for a while now I was able to find a walk-through that worked for me.
http://jarrettmeyer.com/post/2995732471/nested-collection-models-in-asp-net-mvc-3
I think this is the most applicable technique for accomplishing dynamically added nested collection objects for MVC3. Most of the other suggestions I've found were meant for MVC2 or MVC1, and it seems that every iteration of MVC the best way to accomplish this changes slightly.
Hopefully this works for you.
I have the same question. Now looking for solution.
Seems like this resources can help:
http://www.joe-stevens.com/2011/06/06/editing-and-binding-nested-lists-with-asp-net-mvc-2/
http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/
Model binding nested collections in ASP.NET MVC

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.

asp.net MVC "Add View" wizard won't pre-populate fields when using a ViewModel

In VS 2010, When you use the "Add View" wizard to create an Edit view with a strongly typed view, such as Models.Person, the template generates all of Person fields for you.
If you use a view model instead, like this:
public class PersonVM
{
public Person person;
public List<Team> TeamList = new TeamServices().TeamPickList();
...
}
the template wont create all of the fields for Model.person.
Is there a way to make that work?
Not automatically.
Easiest method is to create a new View, select Team as the view data class, Select 'List' as the view content. Then you could cut and paste the markup generated from this view into the one you have already created.
If you use the List template it will normally create a table and iterate over an IEnumerable Model. You can also use one of the helpers and/or custom templates in your CodeTemplates folder:
<% Html.DisplayForModel(); %>
If you need to edit:
<% Html.EditorForModel(); %>
If you're having trouble with the list, maybe start with one of the helpers?
<%: Html.DropDownListFor(model => model.TeamList, new SelectList(Model.TeamList)) %>

Resources