I have a class:
class Item
{
public string Name { get; set; }
public DateTime Date { get; set; }
}
I have a view where I want objects for the class above created using inputs (so I have a textbox for Name and a date select type thing for Date). However, I want thev user to be able to click a link and through jquery/javascript another textbox and date select will be added to the form, and this can happen unlimited times.
How can I bind this to a model so that I can return it to my action method? Ideally the model would be something like:
class MyModel
{
public string AProperty { get; set; }
public List<Item> Items { get; set; }
}
Apologies for the poor wording, struggling to describe what I want but I think this should get the point across.
You want to use a client-side template and then return JSON to your controller. If you are using MVC 3, JSON model binding is built-in, but in MVC 2 you need to set up your own binder. There is an example here.
I recommend using KnockoutJS for your client side. It's very simple for working with dynamic collections and very well documented. You can see an example similar to what you're trying to do here as well as in the previous link.
Related
I have a model with a one-to-many relationship kinda similar to this:
public class people
{
public int id { get; set; }
public sting name { get; set; }
public virtual stuff things { get; set; }
}
public class stuff
{
public int id { get; set; }
public string name { get; set; }
public int thingType { get; set; }
}
I now need to be able to create and edit "stuff" for the person I am currently editing on my "editPeople" page. I'm using AJAX to create JQuery UI Dialog box for the stuff edit/create form, and then I post it back to the server to create a bunch of hidden fields so I can actually save this data later.
I'm concerned that these hidden fields would be named in a way that such that when I post my peopleEdit back to the controller, the Model binding won't properly convert the "stuff" that I have created/edited. Am I doing this wrong/Is there a better method? Am I forced into creating a custom model binder?
In my aproach would be if a people can have many stuff and you want to use ajax you can create a partial view to edit the stuff you would send the stuff object from people to the patial view action and run partial view action on the ajax call.
You may consider a javascript framework like Angular.js or Knockout.js to handle the data binding on the client. Then on submit, you can attach a function and the framework would provide you with the values on the model which you can submit to your server via AJAX.
I often find myself in the situation where I only want to present and edit some fields from my model. Let's say I have a model that represts an address, perhaps I just want the form to update the city and post code fields (bad example, but hopefully it explains the scenario).
I know of two methods:
1) Persist the unwanted fields in hidden input elements on the form, or...
2) Create a dedicated view model that just defines the fields I need.
I favour option #2, but I don't have a nice clean way of merging the data from the view model back into the 'real' model within the controller action. At the moment, I follow this approach...
1) Store the record I'd in a hidden field on the view model
2) When the page posts back, the controller retrieves the original record and I manually assign each field from the view model to the real model
3) Save the real model back to the data store.
This works, but it is quite a lot of work and very easy to miss an assignment/reassignment and I was wondering if anyone knew of another approach?
Use the System.ComponentModel.DataAnnotations.MetadataType.
Something like:
public class BaseClassOfProperties
{
public string Name { get; set; }
}
public interface INameViewableProperties
{
[Display(name = "Your Name")]
string Name { get; set; }
}
public interface INameHiddenProperties
{
//[scaffoldColumn(false)] this completely hides the fields
[UIHint("Hidden")] // i think...
string Name { get; set; }
}
[MetadataType(typeof(INameViewableProperties)]
public class NameViewAbleProperties : BaseClassOfProperties
{
}
[MetadataType(typeof(INameHiddenProperties)]
public class NameHiddenProperties : BaseClassOfProperties
{
}
I use helper #Html.EditorForModel() on all my views.
There is a desire that he skip two fields in my model, but only on this view, the other he must continue to display these fields as usual.
How can I skip these two fields only in this view?
Use the [ScaffoldColumn(false)] attribute.
E.g.
public class Person {
[ScaffoldColumn(false)]
public int PersonID { get; set; }
...
Solution and example sourced from: Pro ASP.NET MVC 3 Framework, Third Edition
I'd recommend writing viewmodels for any view that you want to deviate from default behaviour.
Side note: It's probably a good idea to write a viewmodel for every view, as you get separation of concerns, and it's easier to control the behaviour of each view.
Anyway...
For example, say your model is
class Herps {
public string Name { get; set; }
public int SecretToSomePeople { get; set; }
}
and you don't want to have SecretToSomePeople shown on one of your views, create a viewmodel that doesn't contain SecretToSomePeople
class Herps {
public string Name { get; set; }
}
and use that as the model for the desired view. Make sure you copy to/from the actual model somewhere though.
Strictly speaking, if you don't want to display the fields then they shouldn't be on the Model - the point of Models to to hold exactly the data required for the View.
I have a model that looks like this:
public class Book
{
public string Name { get; set; }
public IEnumerable<Author> Authors { get; set; }
}
public class Author
{
public string FullName { get; set; }
public DateTime BirthDate { get; set; }
}
I have a form in a view for editing a book. The section for editing the Authors collection is in a partial view. The form fields are generated with the Html.EditorFor() method.
It works well for editing existing data. What I would like to do is to put in the Authors editing partial view multiple blank entries that if the user fills them they will be added as new items to the Authors collection.
The final view should look something like this:
http://s1.postimage.org/6g9rqfp20/image.jpg
What is the correct way to achieve this behavior?
If you are using MVC2 this is your best bet
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx#related-results
I am not sure how interested you are in using javascript libraries to get what you are looking to get done, but here is a great example of what you are trying to do: Contact Editor Example
It uses the knockouts library which allows you to work with JavaScript data binding. This also gives you a nice thick application feel on the web which users generally like.
If you are still curious about how this works with serverside, you can look at this presentation from Mix11
Good luck.
My baldness is growing more rapidly than it should be. I first posted this question a couple days ago. I now know the problem and have it working... sort of. Another problem surfaced in it's place.
To solve the previous problem, I manually created the name to requestedDays[{0}].DateOfLeave where {0} was a Guid. This allowed my controller to properly receive the List<> of values.
Using this article's method, the name generated is requestedDays[{0}].DayRequested.DateOfLeave which my controller doesn't properly receive because the name has the class in it, DayRequested.DateOfLeave.
[Authorize, HttpPost]
public ActionResult Create(LeaveRequest leaveRequest, List<DayRequested> requestedDays)
{
}
I have tried to figure out work-arounds with the manual generation, but nothing I have tried works thus far. You can see my validation method here. I do know about the second part of Sanderson's article on validation however, it is quite hard to validate something that isn't being passed into the method.
This is my ViewModel I am using in my partial view.
public class LeaveRequestRow
{
public LeaveRequestRow(DayRequested dayRequested, List<SelectListItem> leaveRequestType)
{
this.DayRequested = dayRequested;
this.LeaveRequestType = leaveRequestType;
}
public List<SelectListItem> LeaveRequestType { set; get; }
public DayRequested DayRequested { set; get; }
}
Does anyone have any ideas on how to proceed? Should I convert my dropdown to a jQuery build control and stop using the ViewModel?
Binding 1-N controller arguments of complex types can be kind of tricky.
Your code examples are not meshing with my fried end of day Friday brain but I'll give it a shot.
Assuming the LeaveRequest class looks like this:
public class LeaveRequest {
public string Text { get; set; }
public string Number { get; set; }
}
The posted form keys must be:
leaveRequest.Text
leaveRequset.Number
That is the easy part. The 1-N binding of a list of DayRequested gets a little weird. Say the DayRequested object looks like this:
public class DayRequested {
public string Words { get; set; }
public string Data { get; set; }
}
Your posted form keys look like:
requestedDays[0].Data
requestedDays[0].Words
requestedDays[1].Data
requestedDays[1].Words
requestedDays[2].Data
requestedDays[2].Words
requestedDays[3].Data
requestedDays[3].Words
The default MVC binder should then trun all 10 form values into your two method arguments ... a POCO and a List of POCOs.
I have solved this, though not as elegantly as I had hoped. All TextBoxFor had to be changed to TextBox along with the addtional changes needed with doing this. The names then were correctly generated and I could move on. This did break the ability for the validation message to appear next to the field, though ValidationSummary still does work. I will be working on fixing that later on and post code samples and a solution on my website.