2

I have only just started using MVC and jQuery validate so please bear with me. I also have no idea what the title of my question should be. 8(

Overview

I am using MVC 4 with jQuery validate. My form is being validated on the client side. I have a scenario where two very alike objects need to be on my form. This has been achieved by means of a ModelView which has two properties. The ModelView is linked to the View and everything works excepting the remote validation. I need to validate a field based on a particular value in the object. Everything is linked together nicely excepting the parameters of the validation action in the controller. Before you give me disapproving tsk tsks, I made up the following code scenario.

The Code

Model class where Name requires remote validation depending on the value of GroupID. Essentially, the name is unique to the group.

public class Colour
{
    [Key]
    public int GroupID {get;set;}

    [Required]
    [Remote("ColourExists", "Validation", AdditionalFields = "GroupID")]
    public string Name {get;set;}
}

Validation controller where the ColourExists action resides.

public class ValidationController :Controller {
    public JsonResult ColourExists(string name, string groupID) {
        // Add validation here later
        return Json(false, JsonRequestBehavior.AllowGet);
    }
}

The View and Controller is linked to a ModelView so that I can display two separate instances on my form. Typically I need to ask the user for a Bright and a Dark colour for one group. (Before you tsk, remember, this isn't for real)

 public class ColourViewModel {
     public Models.Colour BrightColour { get; set; }
     public Models.Colour DarkColour {get;set;}
 }

The generated HTML has input fields BrightColour_Name and DarkColour_Name. These fields have data-val-remote-additionalfields=*.Name attributes. On blur they GET the correct action and controller but the parameters are null. The expected parameters are InstanceName.VariableName such as BrightColour.Name and DarkColour.Name. The request is sent as follows Validation/ColourExists?BrightColour.Name=red&BrightColour.GroupID=10

So how should I pass the values through to the ColourExists action in the validation Controller if my values are linked to variables of an instance?

Edit

The view looks as follows:

@model Colours.ViewModels.ColourViewModel
@using (Html.BeginForm()) {
    @Html.LabelFor(model => model.DarkColour.Name)
    @Html.EditorFor(model => model.DarkColour.Name)
    @Html.HiddenFor(model => model.DarkColour.GroupID)
    <input type="submit" value="Save" />
}
Clarice Bouwer
  • 3,631
  • 3
  • 32
  • 55
  • Can you try pass a single parameter of type `Colour` into your `ColourExists` method? Just a wild guess but maybe MVC is smart enough to serialize the query params into an object... – Tallmaris Jul 09 '13 at 15:48
  • I did a straight `get` request to the controller with `name` and `groupID` and it worked. The moment it is Colour.Name and Colour.GroupID the variables are `null`. I also tried this http://stackoverflow.com/questions/1317523/how-to-use-bind-prefix but couldn't get it to work :( – Clarice Bouwer Jul 09 '13 at 15:52
  • So I managed to get the values via `HttpContext.Request.Params["DarkColour.Name"]`. Is there anything wrong with that method that I may not be aware of? – Clarice Bouwer Jul 09 '13 at 16:59

1 Answers1

1

Normally, in this situation you would use a prefix in your remote validation action like here:

public JsonResult ColourExists([Bind(Prefix = "BrightColour")] string name) {
    // Add validation here later
    return Json(false, JsonRequestBehavior.AllowGet);
}

But, you can't do that in your case, because you are using two same entities in your ViewModel (not ModelView), and each has its own Prefix. So, the binding fails.

So, you'll have to create two separate ViewModels:

public class BrightColourViewModel
{
    public int GroupID { get; set; }
    [Required]
    [Remote("BrightColourExists", "Home", AdditionalFields = "GroupID")]
    public string Name { get; set; }
}

public class DarkColourViewModel 
{
    public int GroupID { get; set; }
    [Required]
    [Remote("DarkColourExists", "Home", AdditionalFields = "GroupID")]
    public string Name { get; set; }
}

Then, redefine your ColourViewModel like here:

public class ColourViewModel
{
    public BrightColourViewModel BrightColour { get; set; }
    public DarkColourViewModel DarkColour { get; set; }
}

And, then create two separate remote validation actions like this:

public JsonResult BrightColourExists(BrightColourViewModel brightColour)
{
    // Call shared code to check if colour exists
    return Json(false, JsonRequestBehavior.AllowGet);
}

public JsonResult DarkColourExists(DarkColourViewModel darkColour)
{
    // Call shared code to check if colour exists
    return Json(false, JsonRequestBehavior.AllowGet);
}
ataravati
  • 8,891
  • 9
  • 57
  • 89
  • Thank you so much for your response. Do you have to do something special to get the Bind attribute to work? If I add it there and access the controller directly the argument is always null. – Clarice Bouwer Jul 10 '13 at 07:38
  • I'll need to see your View. Can you post it here? – ataravati Jul 10 '13 at 13:24
  • I never had a view for my ValidationController. The ValidationController returns a JsonResult. I couldn't get the Bind attribute to work with the controller on its own. – Clarice Bouwer Jul 11 '13 at 11:24
  • "The generated HTML has input fields BrightColour_Name and DarkColour_Name". That's the View I'm referring to. I need to see the View that generates that html page. – ataravati Jul 11 '13 at 13:16
  • I bypassed the view and accessed the controller directly thus taking the view out of the picture entirely and accessed the controller through the browser http://localhost/validation/colourexists. I will update the question with the sample view code. – Clarice Bouwer Jul 11 '13 at 14:09
  • I don't understand why you want to do that. But, if you try the following, the Bind attribute will probably work: `localhost/validation/ColourExists?BrightColour.Name=LightBlue` However, as I said before, you can't use the Bind attribute in your case, because you have two identical entities in your ViewModel. So, you'll have to create two separate ViewModels (like in the answer above), one for BrightColour and one for DarkColour, and create two separate validation actions for each. – ataravati Jul 11 '13 at 15:14
  • I understand that and have found a work around. I just wanted to get the Bind attribute to work because I have never used it before but for some reason could not get it working. I resorted to using the QueryString parameters. The Bind attribute returned null regardless. Thanks for all your help though. – Clarice Bouwer Jul 12 '13 at 07:15