I apologize in advance if this seems like a stupid question but after doing quite a bit of searching around I either can't put the right pieces together or simply haven't found the right answer. Anyways, I've got this model:
public class Resort
{
public int ID { get; set; }
public String Name { get; set; }
public int BlackDiamond { get; set; }
public int BlueSquare { get; set; }
public int GreenCircle { get; set; }
public int TerrainPark { get; set; }
}
And I've got a view that creates TextBoxes as the input for each of those variables. What I need to do is set up some JQuery validation to ensure that each TextBox has a value in it, and more specifically that the TextBoxes for the int has numbers in it.
After doing a little research I'm just not sure how I can even go about setting up the script for the view or if I should rely on Data Annotations in the model?? Any help is appreciated even if it is simply to point me in the right direction of research, I am here to learn. Thank you.
If it's a required field, add the RequiredAttribute to the field:
[Required]
public int BlackDiamond { get; set; }
If you also want a custom message, add it to the attribute:
[Required(ErrorMessage="Please enter a number")]
public int BlackDiamond { get; set; }
If you want built-in jQuery validation, make sure you use the strongly-typed helper:
#Html.TextBoxFor(m => m.BlackDiamond) // you can also use EditorFor
You will need to also include script references to the UnobtrusiveValidation and jQuery Validate plugins to get the automatic validation.
Just FYI, this seems like a good place to start, if you are unfamiliar with validation in MVC: http://msdn.microsoft.com/en-us/VS2010TrainingCourse_ASPNETMVC3FormsandValidation
EDIT: Just to make this answer a bit more complete, as noted in the comments: to see the error messages associated with each control, you need to add the validation helpers: #Html.ValidationMessageFor(m => m.BlackDiamond)
Related
Imagine there is a model defined as:
public class Book
{
public Guid Id { get; set; }
public string Name { get; set; }
public List<Guid> RelatedBooks { get; set; } // List<Book> would be more ideal.
}
I'm trying to add a field in both the Create and Edit views whereby a user will be able to search for related items, in this example by name, and then add them to the new/current item as a related item.
I think this might be possible with a partial view + jQuery, where the partial view acts a search box and returns a list of items. Then with some buttons and jQuery magic these could be added to the current model upon submission of the parent form.
I'm not against using jQuery if it's the only option, but I was wondering if there were any alternatives?
If this is too vague I'm happy to add some more specific details, I've tried search for similar questions but I'm either using the wrong search terms or there aren't any.
Any help would be really appreciated - I'd rather not go down the jQuery + partial view route if there is a cleaner alternative.
First off, you need to create a view-model for what you're trying to achieve that will hold both the list of related books, but also a list of all books we can select.
public class CreateBookViewModel {
public Guid Id { get; set; }
public string Name { get; set; }
public List<Guid> RelatedBooks { get; set; }
public List<Book> AllBooks { get; set; }
}
Populate AllBooks in the Controller and return the view model. Then you could create some magic, maybe with jQuery, to have a select list with all books, that when selected are added to the list of related books by Id. At least you wouldn't need partial views, if you don't want any more functionality than just selecting one or more books.
This is my first time using MVC in a business capacity, so please forgive me if this is an easily answered question.
My issue is that I have a razor view for editing a model, we will call it
MainModel. Here is a reduced version of MainModel:
using System;
using System.Collections.Generic;
namespace MyProject.Models
{
public partial class MainModel
{
public MainModel()
{
SecondModel = new HashSet<SecondModel>();
}
public int MainModelId { get; set; }
public DateTime? DueDate { get; set; }
public int? CreatedByUserId { get; set; }
public DateTime? CreatedDateTime { get; set; }
public bool IsActive { get; set; }
public virtual ICollection<SecondModel> SecondModel { get; set; }
public virtual Users CreatedByUser { get; set; }
}
}
MainModel has a reference to another Model, SecondModel (original names, I know). This reference is a collection so that it can be looped through with a foreach within the razor view. Here is SecondModel:
namespace MyProject.Models
{
public partial class SecondModel
{
public int SecondModelId { get; set; }
public string Comment { get; set; }
public double? Value { get; set; }
public int? ThirdModelId { get; set; }
public bool IsActive { get; set; }
public virtual ThirdModel ThirdModelVariable { get; set; }
}
}
So my issue is that I need to be able to edit the values of variables such as Comment and Value within SecondModel, but from the razor edit page that is using the model from MainModel.
I've tried a few different variation of this, with no luck.
<input asp-for="SecondModel.Comment" class="form-control" />
Usually trying something like this presents the following error:
ICollection<SecondModel> does not contain a definition for 'Comment' and no extension method 'Comment' accepting a first argument of type 'ICollection<SecondModel>' could be found
You may have noticed that there is also a reference to a ThirdModel within SecondModel. I also need to be able to reference some variables from that model within this view, but I assume the solution for one will be the solution for both in this case.
Hopefully I explained that well enough, please let me know if you need to see anymore code to reference or if I wasn't clear enough.
Thanks for your time!
When you specify to what input should be bound, you must specify valid C# accessor (behind the scene this is interpreted as lambda expression which represents access to specific property/field). Error message is telling you that ICollection<SecondModel> does not have property Comment, which is correct since SecondModel property is of type ICollection<SecondModel> and not SecondModel.
To fix your error, you have to retrieve specific element from collection first. So you can either loop the collection and generate input for every element or access specific element if you know it is there. For accessing element you have a problem with your collection type since you cannot use indexer. You should change it to IList<SecondModel>. Then you can for example access write input for comment field for first element in collection like this:
<input asp-for="SecondModel[0].Comment" class="form-control" />
And for accessing ThirdModel:
<input asp-for="SecondModel[0].ThirdModel.SomeProperty" class="form-control" />
I suggest reading Model binding to a list. It is quite old post which does not use tag helpers, but behavior is the same.
ICollection<SecondModel> does not contain a definition for 'Comment'`
The error is clear. You're looking for Comment property in a collection. It should be
#for (int i=0; i< Model.SecondModel.Count(); i++)
{
<input asp-for="SecondModel[i].Comment" class="form-control" />
// other properties
}
or use the HTML Helpers:
#for (int i=0; i< Model.SecondModel.Count(); i++)
{
#Html.TextBoxFor(m => m.SecondModel[i].Comment, new { #class = "form-control" })
// other properties
#Html.TextBoxFor(m => m.SecondModel[i].ThirdModelVariable.Property, new { #class = "form-control" })
}
For the indexers to work, you'd have to change the property from an ICollection<SecondModel> to IList<SecondModel>
To avoid this kind of confusion, whenever you have a IList<T> type property, it's better to name them as plurals. In your case:
public virtual IList<SecondModel> SecondModels { get; set; }
Now you'll know you're referring to the SecondModels property rather than keeping it SecondModel and forgetting it's a collection.
What are good strategies for rebuilding/enriching a nested or complex ViewModel?
A common way to rebuild a flat ViewModel is shown here
But building and rebuilding a nested ViewModel using that method is too complex.
Models
public class PersonInfo
{
public int Id { get; set; }
public string Name { get; set; }
public int Nationality { get; set; }
public List<Address> Addresses { get; set; }
}
public class Address
{
public int AddressTypeID { get; set; }
public string Country { get; set; }
public string PostalCode { get; set; }
}
public class AddressType
{
public int Id { get; set; }
public string Description { get; set; }
}
view models
public class PersonEditModel
{
public int Id { get; set; }
public string Name { get; set; } //read-only
public int Nationality { get; set; }
public List<AddressEditModel> Addresses { get; set; }
public List<SelectListItem> NationalitySelectList { get; set; } //read-only
}
public class AddressEditModel
{
public int AddressTypeId { get; set; }
public string AddressDescription { get; set; } //read-only
public string Country { get; set; }
public string PostalCode { get; set; }
public List<SelectListItem> CountrySelectList { get; set; } //read-only
}
actions
public ActionResult Update(int id)
{
var addressTypes = service.GetAddressTypes();
var person = service.GetPerson(id);
var personEditModel= Map<PersonEditModel>.From(person);
foreach(var addressType in addressTypes)
{
var address = person.Addresses.SingleOrDefault(i => i.AddressTypeId == addressType.Id)
if(address == null)
{
personEditModel.Addresses.Add(new AddressEditModel
{
AddressTypeId = addressType.Id
});
}
else
{
personEditModel.Addresses.Add(Map<AddressEditModel>.From(address));
}
}
EnrichViewModel(personEditModel, person, addressTypes); //populate read-only data such as SelectList
return Index(personEditModel);
}
[HttpPost]
public ActionResult Update(PersonEditModel editModel)
{
if(!ModelState.IsValid)
{
var person = service.GetPerson(editModel.Id);
var addressTypes = service.GetAddressTypes();
EnrichViewModel(editModel, person, addressTypes);
return View(editModel);
}
service.Save(...);
return RedirectToAction("Index");
}
//populate read-only data such as SelectList
private void EnrichViewModel(PersonEditModel personEditModel, Person person, IEnumerable<AddressType> addressTypes)
{
personEditModel.Name = person.Name;
personEditModel.NationalitySelectList = GetNationalitySelectList();
foreach(var addressEditModel in personEditModel.Addresses)
{
addressEditModel.Description = addressTypes.Where(i => i.Id = addressEditModel.AddressTypeId).Select(i => i.Description).FirstOrDefault();
addressEditModel.CountrySelectListItems = GetCountrySelectList(addressEditModel.AddressTypeId);
}
}
My code for building and rebuilding the ViewModels (PersonEditModel and AddressEditModel) is too ugly. How do I restructure my code to clean this mess?
One easy way is to always build a new view model instead of merging/rebuilding since MVC will overwrite the fields with the values in ModelState anyway
[HttpPost]
public ActionResult Update(PersonEditModel editModel)
{
if(!ModelState.IsValid)
{
var newEditModel = BuildPersonEditModel(editModel.Id);
return View(newEditModel);
}
but I'm not sure that this is a good idea. Is it? Are there other solutions besides AJAX?
I'm going to tackle your specific pain points one-by-one and I'll try to present my own experience and likely solutions along the way. I'm afraid there is no best answer here. You just have to pick the lesser of the evils.
Rebuilding Dropdownlists
They are a bitch! There is no escaping rebuilding them when you re-render the page. While HTML Forms are good at remembering the selected index (and they will happily restore it for you), you have to rebuild them. If you don't want to rebuild them, switch to Ajax.
Rebuilding Rest of View Model (even nested)
HTML forms are good at rebuilding the whole model for you, as long as you stick to inputs and hidden fields and other form elements (selects, textarea, etc).
There is no avoiding posting back the data if you don't want to rebuild them, but in this case you need to ask yourself - which one is more efficient - posting back few extra bytes or making another query to fetch the missing pieces?
If you don't want to post back the readonly fields, but still want the model binder to work, you can exclude the properties via [Bind(Exclude="Name,SomeOtherProperty")] on the view model class. In this case, you probably need to set them again before sending them back to browser.
// excluding specific props. note that you can also "Include" instead of "Exclude".
[Bind(Exclude="Name,NationalitySelectList")]
public class PersonEditModel
{
...
If you exclude those properties, you don't have to resort to hidden fields and posting them back - as the model binder will simply ignore them and you still will get the values you need populated back.
Personally, I use Edit Models which contain just post-able data instead of Bind magic. Apart from avoiding magic string like you need with Bind, they give me the benefits of strong typing and a clearer intent. I use my own mapper classes to do the mapping but you can use something like Automapper to manage the mapping for you as well.
Another idea may be to cache the initial ViewModel in Session till a successful POST is made. That way, you do not have to rebuild it from grounds up. You just merge the initial one with the submitted one in case of validation errors.
I fight these same battles every time I work with Forms and finally, I've started to just suck it up and go fully AJAX for anything that's not a simple name-value collection type form. Besides being headache free, it also leads to better UX.
P.S. The link you posted is essentially doing the same thing that you're doing - just that its using a mapper framework to map properties between domain and view model.
Hi I have following in my Asp.net MVc Model
TestModel.cs
public class TestModel
{
public double OpeningAmount { get; set; }
[Required(ErrorMessage="Required")]
[Display(Name = "amount")]
[Range(0 , double.MaxValue, ErrorMessage = "The value must be greater than 0")]
public string amount { get; set; }
}
Now from my controller "OpeningAmount " is assign .
Finaly when I submit form I want to check that "amount" must be greater than "OpeningAmonut" . so want to set Range dynamically like
[Range(minimum = OpeningAmount , double.MaxValue, ErrorMessage = "The value must be greater than 0")]
I do not want to use only Jquery or javascript because it will check only client side so possible I can set Range attribute minimum dynamically than it would be great for.
Recently there's been an amazing nuget that does just that: dynamic annotations and it's called ExpressiveAnnotations
It allows you to do things that weren't possible before such as
[AssertThat("ReturnDate >= Today()")]
public DateTime? ReturnDate { get; set; }
or even
public bool GoAbroad { get; set; }
[RequiredIf("GoAbroad == true")]
public string PassportNumber { get; set; }
There's no built-in attribute which can work with dependence between properties.
So if you want to work with attributes, you'll have to write a custom one.
Se here for an example of what you need.
You can also take a look at dataannotationsextensions.org
Another solution would be to work with a validation library, like (the very nice) FluentValidation .
I'm using ASP.NET MVC 3 and Entity Framework 4.1 Code First.
Let's say I have a User entity :
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
When editing it in my UserController I want to add a PasswordConfirmation field and verify that PasswordConfirmation == Password
1. By composition
My first try was :
public class EditUserModel
{
[Required]
public User User { get; set; }
[Compare("User.Password", ErrorMessage = "Passwords don't match.")]
public string PasswordConfirmation { get; set; }
}
In this case the client side validation works but (Edit: client side validation working was a coincidence.) doesn't work and the server side validation fails with the following message : Could not find a property named User.Password
Edit: I think the best solution, in this case, would be to create a custom CompareAttribute
Implementing IValidatableObject
public class EditUserModel : IValidatableObject
{
[Required]
public User User { get; set; }
public string PasswordConfirmation { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if(this.PasswordConfirmation != this.User.Password)
return new[] { new ValidationResult("Passwords don't match", new[] { "PasswordConfirmation " }) };
return new ValidationResult[0];
}
}
In this case the server side validation works but the client side validation doesn't work anymore. Implementing IClientValidatable seems a bit too complicated and I prefer not having client side validation in this case.
2. By inheritance
public class EditUserModel : User
{
[Compare("Password", ErrorMessage = "Passwords don't match.")]
public string PasswordConfirmation { get; set; }
}
When trying to directly save EditUserModel using EF it doesn't work, I get some some error message about the EditUserModel metadata so I'm using AutoMapper to convert from User to EditUserModel and backwards.
This solution works but it more complex because I have to convert from the model to the view model and backwards.
3. By duplication
(Suggested by Malte Clasen)
The view model would have all the properties of the model plus additional ones. AutoMapper can be used to convert from one to another.
public class EditUserModel {
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
[Compare("Password", ErrorMessage = "Passwords don't match.")]
public string ConfirmPassword { get; set; }
}
This is the solution I like the least because of code duplication (DRY)
Questions
What are the pros and cons of inheritance, composition and duplication in this case ?
Is there a simple way to have both client side and server side validation without having to convert the model to the view model and backwards ?
Having struggled with this question before, I have in various instances gone with all three. In general, most of the opinions I've seen favor duplication in an MVC project, with a ViewModel constructed specifically for each view. In this manner the convention you'd use is something like UserDetailsViewModel and UserCreateViewModel. As you said, at that point AutoMapper or some other auto mapping tool would be used to convert from your domain objects to these flat ViewModels.
While I, too, don't like repeating code, I also don't like polluting my domain objects with validation or other view-specific attributes. Another advantage, though admittedly one almost nobody would ever have to contend with (regardless of what all the pros say), is that you can manipulate your domain objects in some ways without necessarily manipulating your ViewModels. I mention that because it's commonly cited, not because it carries much weight for me.
Lastly, using a truly flat ViewModel makes for cleaner markup. When I've used composition, I've often made errors creating HTML elements with names that are something like User.Address.Street. A flat ViewModel reduces at least my likelihood of doing that (I know, I could always use HtmlHelper routines to create elements, but that's not always feasible).
My recent projects have also pretty much required separate ViewModels these days anyway. They've all been NHibernate-based, and the use of proxies on NHibernate objects makes it not possible to use them directly for views.
Update - here's a good article I've referred to in the past: http://geekswithblogs.net/michelotti/archive/2009/10/25/asp.net-mvc-view-model-patterns.aspx
You could also consider independent classes for domain and view models, in this case for example
public class EditUserModel {
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string ConfirmPassword { get; set; }
}
if the Id is stored in the url. If you want to avoid the manual copy between the instances of User and EditorUserModel, AutoMapper can help you. This way you can easily decouple the password string in your view model from the password hash in your domain model.
I have trying to work this out and I found a solution that does not involve duplicating code. It's kind of workaround but, in my opinion, it's better than the other proposed solutions.
You have the User Model with all the validation:
public class UserModel
{
[Required]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
You compose the previous model with a new model
public class EditUserModel
{
public UserModel User { get; set; }
[Required]
public string PasswordConfirmation { get; set; }
}
The trick is in the action, you could receive more than one model:
[HtttPost]
public ActionResult UpdateInformation(UserModel user, EditUserModel editUserModel) {
if (ModelState.IsValid) {
// copy the inner model to the outer model, workaround here:
editUserModel.User = user
// do whatever you want with editUserModel, it has all the needed information
}
}
In this way the validation works as expected.
Hope this helps.
I don't use Entity Models too much, I prefer LINQ - SQL models so this may be incorrect:
Why not use a meta-data class which is applied to the Entity?
With LINQ - SQL the metadata assigned is taken into consideration for both client-side as well as server-side validation.
From what I understand application of a [MetaDataType] attribute is similar to inheritance only it works without implementing a new class (model) for alterations to the basic entity.
Also, another option you might want to try is creating a custom attribute - I did this once for a similar purpose. Essentially a flag which indicated the persistence of a member.
So i would have an entity defined as follows:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
[DoNotPersist]
public string ConfirmPassword {get; set;}
}
Also, I don't know what you are doing to store data but I had hooked an override into the OnInserting , OnEditing, OnDeleting functions for my DataContext which basically removed any members having my custom attribute.
I like this method simple because we use a lot of temporary, rather algorithmic data for each model (building good UI's for Business Intelligence) which is not saved in the database but is used everywhere inside model functions, controllers, etc - so we use dependency injection in all model repositories and controllers and so we have all these extra data points for each table to play with.
Hope that helps!
PS:- Composition vs Inheritance - it really depends on the target user of the application. If it is for an intranet app where security is less of an issue and the user / browser environment is controlled then just use client side validation, ie: composition.
I would favour composition over inheritance.
In case of your user password it looks like you're actually storing the password in Users table in clear text, which is VERY, VERY BAD.
You should store only a salted hash, and your EditUserModel should have two string properties for password and password confirmation, which are NOT the fields in your table.