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.
Related
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 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.
I'm writing a message board webpage. The page consists of a Topic item, then a list of Response and a form to add an additional response.
Im struggling to structure my page and viewdata classes in such a way that they are clean and allow me to take advantage for editor templates and validation attributes.
Currently I have one page to do all the above, and Im thinking my viewdata class will eventually look something like this:
public class TopicViewsData
{
[ValidateNonEmpty("Please enter some text")]
public string Title { get; set; }
[ValidateNonEmpty("Please enter some text")]
public string TopicBody { get; set; }
public IList<TopicResponseViewsData> Responses { get; set; }
public TopicResponseViewsData NewResponse { get; set; }
}
public class TopicResponseViewsData
{
[ValidateNonEmpty("Please enter some text")]
public string ResponseText{ get; set; }
}
My page is typed to a TopicViewsData, it just seems ugly that I have to have NewResponse property just so the page can have access to the validation attributes on TopicResponseViewsData. Is there a nicer way to do this?
Sounds like you are headed towards a massive and complex view, not to mention the issues you are already seeing with your model structuring. Rather than making trade offs to make what you have work I have a few recommendations on your overall view model design.
I tend to separate my models into ViewModels and FormModels. ViewModels are for displaying data and FormModels are for taking user input. Not only does this provide a clear designation of function it generally allows me to keep my FormModel properties typed to primitives, strings, and dates in addition to providing a single place for applying validation logic. While, in my ViewModels I am afforded the flexibility to use complex property types and do not have to worry about validation logic.
To make things even easier I follow Jimmy Bogard's suggestion that you should have only one view per model. By not mixing and matching models I have found my models stay focused and my views do not turn into spaghetti. To keep things tidy I name my models similarly to the Controller and View they are tied to. I might end up with a few extra models, but it is a small price to pay for a cleaner design.
I think that the Body property in the TopicViewsData model is redundant with the NewResponse property.
So your view is working with responses where each response has a body. So:
public class TopicResponseViewsData
{
[ValidateNonEmpty("Please enter some text")]
public string Body { get; set; }
}
So far so good. Next you said that you have a list of responses to show and a new response to add, so:
public class TopicViewsData
{
public IList<TopicResponseViewsData> Responses { get; set; }
public TopicResponseViewsData NewResponse { get; set; }
}
For the moment, given your description that's all I see necessary in the view model. At least model reflects your scenario description.
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.
OK, So i have been watching some MVC vids and reading some bits.
I am new to the entire MVC pattern, and until now have been happily wrapped up in the web forms world!
Like with so many demos it all seems great and I'm sure I'll have lots I dont understand as I move along, but in the first instance...
I can see that you can have a strongly typed view, which gets data from the controller. What happens if I want data in a view from different object types?? Say i want to show a grid of cars and a grid of people, which are not related in anyway??
Thx
Steve
Setup your strongly typed ViewData class with two properties like this
public class MyViewData
{
public IEnumerable<Car> Cars { get; set; }
public IEnumerable<People> People { get; set; }
}
and then fill them in the controller,
Sorry for the duplicate. In good MVC spirit try to use interfaces where possible to make your code more generic
Instead of artificially grouping models together you could keep then separate (logically and physically) and then in the view pull the various pieces together.
Check out this post for the a great explanation of [link text][1].
[1]: http://blog.codeville.net/2008/10/14/partial-requests-in-aspnet-mvc/ partial-requests
You can either pass both objects inside the ViewData hashtable, or create a MyViewViewModel, add two properties, and set them both from your controller.
What I think would be best to do in this situation would be create a class in the Models folder to hold both of these types.
Example:
public class CarsPeopleModel
{
public List<Car> Cars { get; set; }
public List<Person> People { get; set; }
}
Then your view would be:
public partial class Index : ViewPage<MvcApplication1.Models.CarsPeopleModel>
{
}