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?

2 Answers 2

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);
}
Sign up to request clarification or add additional context in comments.

3 Comments

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, 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.
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.
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.

2 Comments

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 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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.