MVC3 Remote Validation of a field within a List

2.9k views Asked by At

For some reason, whenever I try to remotely validate a primitive property inside an array within my model, the value of the property is not passed as a parameter.

For example, when my remote validation method (UniqueItemNo) is called, the string parameter "id" is always null. If I were to perform validation on CartNumber instead of ItemNumber, the parameter is passed correctly.

Controller

public class HomeController : Controller
{
    public ActionResult Index()
    {
        CartModel cart = new CartModel();
        cart.Items.Add(new ItemModel() { ItemNumber = "12345" });
        return View(cart);
    }

    [HttpPost]
    public JsonResult UniqueItemNo(string id)
    {
        /** Do Work **/
        return null;
    }
}

Models

public class ItemModel
{
    [Remote("UniqueItemNo", "Home", HttpMethod="POST")]
    public string ItemNumber { get; set; }
}

public class CartModel
{
    public CartModel()
    {
        Items = new List<ItemModel>();
    }

    public List<ItemModel> Items { get; set; }
    public string CartNumber { get; set; }
}

View

@{
    ViewBag.Title = "Home Page";
}

<h2>@ViewBag.Message</h2>
@using(Html.BeginForm()) {
    <p>
        @for(int i = 0; i < Model.Items.Count; i++)
        {
            @Html.TextBoxFor(m => m.Items[i].ItemNumber);
            @Html.ValidationMessageFor(m => m.Items[i].ItemNumber);                                    
        }
    </p>
}
4

There are 4 answers

10
Tom Chantler On

Try changing the parameter to match the model. Like this:

    [HttpPost]
    public JsonResult UniqueItemNo(ItemModel itemModel)
    {
        // look at itemModel.ItemNumber...
        /** Do Work **/
        return null;
    }

You can still start the name of the ActionMethod parameter with lowercase (itemNumber instead of ItemNumber) as per the normal coding conventions.

EDIT: Have you tried coding the View like this?

@{
    ViewBag.Title = "Home Page";
}

<h2>@ViewBag.Message</h2>
        @foreach (var item in Items)
        {
           @using(Html.BeginForm()) {
              <p>
                 @Html.HiddenFor(m=>m.CartNumber)
                 @Html.TextBoxFor(m => item.ItemNumber);
                 @Html.ValidationMessageFor(m => item.ItemNumber);                                    
              </p>
           }
        }
0
Guilherme Melo On

I hope you found an answer already, but in case you didn't, try this:

[HttpPost]
public JsonResult UniqueItemNo()
{
    var itemNumber = Request.Form[Request.Form.AllKeys.First(p => p.Contains("ItemNumber"))];

    /** Do Work **/
    return Json(true);
}

Note that @Mamoon ur Rasheed answer was almost correct, but since you're using POST your parameters will be inside the Form property, not QueryString.

Let me know if you still have any issues regarding this.

Btw, you should use a try/catch. I didn't wrote it just to keep it simple.

0
alexb On

Unfortunately you don't have support for this.

In client side the remote validation is handled by jquery.validate.unobtrusive.js which says

...
$.each(splitAndTrim(options.params.additionalfields || options.element.name), function (i, fieldName) {
    var paramName = appendModelPrefix(fieldName, prefix);
    value.data[paramName] = function () {
        return $(options.form).find(":input[name='" + escapeAttributeValue(paramName) + "']").val();
    };
});
...

so you should notice that for each additional field it constructs just one parameter in query string (or in form if you do post). because of that you server side remote attribute should look like

[Remote("UniqueItemNo", AdditionalFields = "ItemNumber[0],ItemNumber[1],...")]

fact that you don't know at design time.

you could use a js hack and keep a hidden field named ItemNumber and with value a concatenated list of ids. on server side you should change the type of ItemNumber array into string and split the values.

I hope this helps although it's a long time since you asked.

1
Mamoon ur Rasheed On

just solved the same problem. so for my type of people who are going to have this problem in future, here is the solution

here is the root cause.

when you do @Html.TextBoxFor(x=>x.m.Items[i].ItemNumber) it outputs something like this

<input type=text value='value' name='items[index].ItemNumber' ...

and then it adds the other validations like data-* attributes for validation. e.g. remote validation url data-remot-url and etc

now when form got validated it requested the remote url with data like key=xxx[1].propertyname

and this is not assignable to a single variable in remote action method that will receive this property.

if you will change the action arguments to something like this, then it will work

 public JsonResult UniqueItemNo(list<string> propertyname)

and even then you are not guranteed as it requires to have the parameters index start from 0 and without any gap. so xxx[1].ItemNumber will never get maped.

the only remaining solution is to get the property name from querystring and map it to your required variable. like this

    if(Request.QueryString.AllKeys.FirstOrDefault(p => p.ToLower().Contains("propertyname"))!=null)
    {
                     propertyname = Request.QueryString[Request.QueryString.AllKeys.First(p => p.ToLower().Contains("propertyname"))];
}

it will search and map the required variables.

hope this will help