2011-05-31 8 views
6

Estoy tratando de mostrar un modelo de vista usando una plantilla de editor que envuelve el modelo en un conjunto de campos antes de aplicar una plantilla de editor de objetos base.¿Se puede pasar un modelo a través de varias plantillas de editor?

Mi opinión:

@model Mvc3VanillaApplication.Models.ContactModel 

@using (Html.BeginForm()) 
{ 
    @Html.EditorForModel("Fieldset") 
} 

utiliza una plantilla de campos (Visto/Común/EditorTemplates/Fieldset.cshtml):

<fieldset> 
    <legend>@ViewData.ModelMetadata.DisplayName</legend> 
    @Html.EditorForModel() 
</fieldset> 

que a su vez utiliza una plantilla básica para todos los objetos (Views/Shared/EditorTemplates/Object.cshtml):

@foreach (var prop in ViewData.ModelMetadata.Properties.Where(x => 
    x.ShowForEdit && !x.IsComplexType && !ViewData.TemplateInfo.Visited(x))) 
{ 
    @Html.Label(prop.PropertyName, prop.DisplayName) 
    @Html.Editor(prop.PropertyName) 
} 

Esa es mi intención de todos modos. El problema es que mientras la página se representa con un campo y una leyenda, la plantilla de objeto no se aplica, por lo que no se muestran controles de entrada.

Si cambio la vista para no especificar la plantilla "Fieldset", las propiedades de mi modelo se representan utilizando la plantilla Object, por lo que no es posible que no se encuentre mi plantilla Object.

¿Es posible pasar el mismo modelo a través de varias plantillas?

Por lo que vale la pena, el modelo de vista se parece a esto:

namespace Mvc3VanillaApplication.Models 
{ 
    [System.ComponentModel.DisplayName("Contact Info")] 
    public class ContactModel 
    { 
     public string FirstName { get; set; } 
     public string LastName { get; set; } 
    } 
} 
+0

Así complican. ¿Puedes publicar tu código de modelo de vista? Para que podamos ser más claros acerca de su pregunta. Como sabía, tratas de hacer un componente común para usar para todos los editores, ¿verdad? – thangchung

+0

@ThangChung Bueno, estoy sobreescribiendo la plantilla del editor de objetos porque es pesada (envolviendo todo en divs) y quiero extenderla para elegir otras convenciones. Más allá de eso, estoy usando la plantilla fieldset para que cada modelo se muestre en un fieldset que se convierte en una página en un asistente a través de javascript. –

Respuesta

5

I implementado lo que tienes, y fue capaz de reproducirla. Establecí un punto de interrupción en Object.cshtml para poder inspeccionarlo y me pilló desprevenido al darme cuenta de que ni siquiera estaba llegando a la plantilla del objeto cuando se estaba utilizando la plantilla fieldset. Luego di un paso adelante en la plantilla fieldset y vi que estaba llamando a la plantilla, así que algo debe estar ocurriendo en el código que le impide mostrar la plantilla del objeto.

Abrí el MVC3 source code, busqué EditorForModel y encontré la función correcta.

public static MvcHtmlString EditorForModel(this HtmlHelper html) { 
    return MvcHtmlString.Create(TemplateHelpers.TemplateHelper(html, html.ViewData.ModelMetadata, String.Empty, null /* templateName */, DataBoundControlMode.Edit, null /* additionalViewData */)); 
} 

Obviamente, esto no es cierto, por lo que presiona F12 en TemplateHelpers.TemplateHelper, y una vez allí de nuevo me presionó F12 de guardia de una sola línea, que le lleva a la carne de la función. Aquí me encontré con este breve fragmento de código a partir de la línea 214 de TemplateHelpers.cs:

// Normally this shouldn't happen, unless someone writes their own custom Object templates which 
// don't check to make sure that the object hasn't already been displayed 
object visitedObjectsKey = metadata.Model ?? metadata.RealModelType; 
if (html.ViewDataContainer.ViewData.TemplateInfo.VisitedObjects.Contains(visitedObjectsKey)) { // DDB #224750 
    return String.Empty; 
} 

Esos comentarios son en realidad en el código, y aquí tenemos la respuesta a su pregunta: Puede uno modelo de pasar a través de varias plantillas editor ?, la respuesta es no *.

Dicho esto, este parece ser un caso de uso muy razonable para tal característica, por lo que encontrar una alternativa probablemente valga la pena. Sospeché que un delegado de afeitar con plantillas resolvería esta funcionalidad de envoltura, así que lo probé.

@{ 
    Func<dynamic, object> fieldset = @<fieldset><legend>@ViewData.ModelMetadata.DisplayName</legend>@Html.EditorForModel()</fieldset>; 
} 

@using (Html.BeginForm()) 
{ 
    //@Html.EditorForModel("Fieldset") 
    //@Html.EditorForModel() 
    @fieldset(Model) 
} 

Y viola! ¡Funcionó! Dejaré que usted lo implemente como un método de extensión (y mucho más reutilizable).Aquí hay una breve publicación en el blog sobre templated razor delegates.


* Técnicamente se podría reescribir esta función y compilar su propia versión de MVC3, pero es probablemente más problemas de lo que vale la pena. Intentamos hacer esto en el proyecto careers cuando descubrimos que la función Html.ActionLink es bastante lenta cuando tiene unos cientos de rutas definidas. Existe un problema de firma con el resto de las bibliotecas, el cual decidimos que no valía la pena el tiempo para trabajar ahora y mantenerlo para versiones futuras de MVC.

+1

Gracias por la respuesta completa; un método de extensión parece ser el camino a seguir. Miré en la fuente y noté (TemplateInfo.cs: 33) que se había agregado el HashSet de VisitedObjects para evitar la recursión infinita. Parece que podría ser mejor si el equipo de MVC usara una combinación del modelo y la plantilla en el conjunto en lugar del modelo exclusivamente. De esta forma, se evitaría la recursión pero se permitirían la aplicación de varias plantillas. Sin embargo, me mantendré alejado de rodar mi propia versión de MVC. –

+0

@EricBock: Otra alternativa podría ser ajustar el modelo en un 'FieldsetViewModel' que solo tiene 1 propiedad, un objeto. Aunque parece un poco engorroso. –

0

En primera plantilla cshtml podemos recrear ViewData.TemplateInfo (y la lista VisitedObjects claro)

var templateInfo = ViewData.TemplateInfo; 
ViewData.TemplateInfo = new TemplateInfo 
{ 
    HtmlFieldPrefix = templateInfo.HtmlFieldPrefix, 
    FormattedModelValue = templateInfo.FormattedModelValue 
}; 

Ahora podemos llamar a otra plantilla con mismo modelo

@Html.DisplayForModel("SecondTemplate") 
Cuestiones relacionadas