2011-04-01 11 views
5

La validación remota funciona bien cuando tengo solo una instancia de mi modelo en la vista.Validación remota con AdditionalFields en la colección de modelos

El problema es cuando mi vista se trata de la colección de modelos. Aquí está mi modelo:

public class TableFormTestModel 
{ 
    public GridRow[] GridData { get; set; } 
    public class GridRow 
    { 
     public Int32 Id { get; set; } 

     [Required, StringLength(50), Remote("IsNameAvailable", "TableFormTest", "Admin", AdditionalFields = "Id")] 
     public String Name { get; set; } 
    } 
} 

En mi opinión Tengo:

@model TableFormTestModel 
@using (Html.BeginForm()) 
{ 
    Html.EnableClientValidation(); 
    Html.EnableUnobtrusiveJavaScript(); 
    for(var i = 0;i<Model.GridData.Length;i++) 
    { 
    <div> 
     @Html.HiddenFor(x => Model.GridData[i].Id) 
     @Html.TextBoxFor(x => Model.GridData[i].Name) 
     @Html.ValidationMessageFor(x => Model.GridData[i].Name)  
    </div> 
    } 
} 

Esto es bastante lejos de generar la forma, ¿alguien puede mejorar la sintaxis para mí por favor?

siguiente formulario HTML se produce:

<form method="post" action="/Admin/TableFormTest/"> <div> 
    <input type="hidden" value="1" name="GridData[0].Id" id="GridData_0__Id" data-val-required="The Id field is required." data-val-number="The field Id must be a number." data-val="true"> 
    <input type="text" value="abc" name="GridData[0].Name" id="GridData_0__Name" data-val-required="The Name field is required." data-val-remote-url="/Admin/TableFormTest/IsNameAvailable" data-val-remote-additionalfields="*.Name,*.Id" data-val-remote="&amp;#39;Name&amp;#39; is invalid." data-val-length-max="50" data-val-length="The field Name must be a string with a maximum length of 50." data-val="true"> 
    <span data-valmsg-replace="true" data-valmsg-for="GridData[0].Name" class="field-validation-valid"></span>  
</div> 
<div> 
    <input type="hidden" value="2" name="GridData[1].Id" id="GridData_1__Id" data-val-required="The Id field is required." data-val-number="The field Id must be a number." data-val="true"> 
    <input type="text" value="def" name="GridData[1].Name" id="GridData_1__Name" data-val-required="The Name field is required." data-val-remote-url="/Admin/TableFormTest/IsNameAvailable" data-val-remote-additionalfields="*.Name,*.Id" data-val-remote="&amp;#39;Name&amp;#39; is invalid." data-val-length-max="50" data-val-length="The field Name must be a string with a maximum length of 50." data-val="true"> 
    <span data-valmsg-replace="true" data-valmsg-for="GridData[1].Name" class="field-validation-valid"></span>  
</div> 

Aunque HTML anterior se ve bastante bien (cada Modelo de la colección tiene identificación única y nombre) hay un problema con los campos adicionales en la validación remota :

El ID de la primera fila se recoge cuando se activa la validación remota en la segunda fila

+0

Pruebe usar DisplayTemplates y luego @Html.DisplayFor (modelo => modelo.GridData) – mimo

Respuesta

1

En primer lugar, sí, puede mejorar la sintaxis de su vista. Utilice EditorTemplates.

Crear Views\Shared\EditorTemplates\GridRow.cshtml:

@model TestMvc.Models.TableFormTestModel.GridRow 
<div> 
    @Html.HiddenFor(x => x.Id) 
    @Html.TextBoxFor(x => x.Name) 
    @Html.ValidationMessageFor(x => x.Name) 
</div> 

Ahora su vista principal sólo necesita ser:

@model TableFormTestModel 
@using (Html.BeginForm()) 
{ 
    Html.EnableClientValidation(); 
    Html.EnableUnobtrusiveJavaScript(); 
    @Html.EditorFor(x => x.GridData) 
} 

En cuanto a los problemas RemoteAttribute, es complicado. El problema se debe a los nombres de las entradas que MVC crea para las matrices. Como puede ver, sus entradas se nombran, por ejemplo, GridData[1].Id, GridData[1].Name (etc.). Bueno, jQuery hace su llamada ajax al proporcionar esos nombres a la cadena de consulta.

Por lo tanto, lo que termina siendo llamado es

/Admin/TableFormTest/IsNameAvailable?GridData%5B1%5D.Name=sdf&GridData%5B1%5D.Id=5 

aka

/Admin/TableFormTest/IsNameAvailable?GridData[1].Name=sdf&GridData[1].Id=5 

... y el aglutinante modelo por defecto realmente no sabe qué hacer con eso.

Lo que sugiero es que escriba su propio encuadernador de modelo personalizado. Dile a MVC cómo leer esta cadena de consulta y luego crea el objeto que deseas.

Aquí hay una prueba de concepto. (Pero no use esta cosa en la producción: toma demasiadas suposiciones y estrellarse y arder en cualquier cosa inesperada.)

public class JsonGridRowModelBinder : IModelBinder { 

    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { 
     var model = new TableFormTestModel.GridRow(); 
     var queryString = controllerContext.HttpContext.Request.QueryString; 
     model.Name = queryString[queryString.AllKeys.Single(x => x.EndsWith("Name"))]; 
     string id = queryString[queryString.AllKeys.Single(x => x.EndsWith("Id"))]; 
     model.Id = string.IsNullOrWhiteSpace(id) ? 0 : int.Parse(id); 

     return model; 
    } 

} 

A continuación, informe a su método de IsNameAvailable utilizar este modelo de aglutinante:

public JsonResult IsNameAvailable([ModelBinder(typeof(JsonGridRowModelBinder))] TableFormTestModel.GridRow gridRow) { 
    ... 
} 
Cuestiones relacionadas