0

I have a really simple ASP.NET MVC website where users can browse products and place a pre-order. To place it they have to specify the name and email address. The controller looks like this:

[HttpGet]
public ActionResult Product(string id)
{
    return View(new ProductViewModel(id));
}

[HttpPost]
public ActionResult Product(PreOrderProductCommand command)
{
    if (ModelState.IsValid)
    {
        command.Process();
        return View("ThankYou");
    }
    else
        return Product(command.Id);
}

ProductViewModel contains various product info (name, description, price, etc.) and PreOrderProductCommand contains just 3 string properties: Id, Name and Email. Now I want to enable client side validation of Name and Email using jquery.validate and can't figure out how to implemet it. All the tutorials on the web say that I have to use code like this:

@Html.ValidationMessageFor(model => model.Email)

But the problem is that my view has ProductViewModel as a model and this class doesn't have the property Email. This property and its validation logic reside in PreOrderProductCommand.

What's the right way to implement client-side validation in this case? What am I missing?

holdenmcgrohen
  • 1,031
  • 2
  • 9
  • 30

2 Answers2

1

No, for client side validation you should add Email property to your ProductViewModel.

That happened becouse HtmlHelpers create data-validation attrributes in inputs according to Attributes above ViewModel properties and then jquery.validate check this propeties.

Your server side validation works becouse it uses different mechanism. It checks validation properties on server after binding request properties to your model (in your case PreOrderProductCommand model). Bunding happends according to Names of properties so if you have right names everything should be fine.

The only other way to do it is to create markup manually with validation attridutes that you need. (I mean plain html in your View) But i wouldn't recommend it.

teo van kot
  • 12,350
  • 10
  • 38
  • 70
  • If I add `Email` to `ProductViewModel` I have to copy all the validation attributes from `PreOrderProductCommand` and I end up with 2 sets of identical validation rules in 2 different classes. That seems very inconvenient. – holdenmcgrohen Oct 29 '15 at 14:51
  • @holdenmcgrohen you can define `abstract` class and inherit command and viewmodel from it. But i think that's not worth it. Unfortunatly you can't inherit attributes from `interface`. And i think sometimes it's ok sometimes have some dublication in code. Another thing that you can think about is always have the same class on view and on post controller (now you have 2 different calases) and mabe some kind of mapper – teo van kot Oct 29 '15 at 21:21
1

You need to add the Email property to your ProductViewModel. With the following attributes:

[DisplayName("Email")]
[Required(ErrorMessage = "Please enter email")]
public string email { get; set; }

Then pass this model into your HttpPost controller

and populate your command class from within i.e.

[HttpPost]
public ActionResult Product(ProductViewModel model)
{

PreOrderProductCommand command = new PreOrderProductCommand();

command.Id = model.id;    
command.Email = model.email;

if (ModelState.IsValid)
{
    command.Process();
    return View("ThankYou");
}
else
    return Product(command.Id);
}
Dez79
  • 527
  • 2
  • 9
  • 25
  • This will work, but I'm trying to separate the view model (which contains product info) from user input. Those 2 things are logically very different and it doesn't make sense to put them into a single object. I was hoping there was a way to keep them separate and make client validation work. – holdenmcgrohen Oct 29 '15 at 16:42
  • 1
    @holdenmcgrohen, the viemodel should hold whatever is in the view, icluding user details if necessary, you can then work with your second class within the controller, see edit above. – Dez79 Oct 29 '15 at 17:28
  • I'm not sure I like the idea of copying data from model to command like this: `command.Email = model.email`. It's ok if you only have to copy 2 properties, but suppose you have 20 of them. I'm trying to figure out a clean way to separate view models (what I show to the user) from domain logic (what I do with user input), but it seems harder than I thought. – holdenmcgrohen Oct 29 '15 at 19:32