2009-05-29 21 views
24

Esta es una pregunta de diseño general: ¿Cómo implementaría un formulario dinámico (generado en tiempo de ejecución) en ASP.NET MVC?Formularios dinámicos (generados en tiempo de ejecución) en ASP.NET MVC

Esta es la situación:

  1. Un administrador del sitio puede definir parámetros de formulario (campos, tipo de campos, validación) con una interfaz gráfica de usuario (vista MVC).
  2. Según sea necesario, el tiempo de ejecución genera el formulario para el usuario final en función de la configuración del administrador. Supongo que toda esta lógica residiría en el controlador, o quizás en métodos de extensión, filtros de acción o algo así.
  3. El usuario final rellena el formulario, las visitas se envían, la información se captura en la base de datos.

La personalización no necesita admitir controles anidados, controles de terceros, etc., pero sospecho que un diseño muy elegante permitiría eso. Principalmente, solo necesito que el administrador pueda especificar campos adicionales como cuadros de texto, casillas de verificación, botones de opción y cuadros combinados. También necesitaré que la aplicación asigne un espacio para que estos datos se guarden en el archivo db, pero creo que tengo esa parte resuelta.

Gracias por la ayuda.

+0

Me opongo a cerrar esta pregunta. La arquitectura detrás de la creación de formularios generados en tiempo de ejecución * en un marco específico del servidor * es amplia, pero es claramente responsable, como lo demuestran las respuestas a continuación. ¿Están todas las preguntas de arquitectura fuera del tema de este sitio simplemente porque las decisiones de arquitectura son de alto nivel y tienen ventajas y desventajas? –

Respuesta

1

Una forma de hacerlo es crear su propio ModelBinder que sería el corazón de sus formularios generados. Un encuadernador es responsable de validar el ModelState y reconstruir el ViewDataModel mecanografiado (suponiendo que sus puntos de vista están escritos).

El modelo ligante DataAnnotations podría ser una buena referencia para este modelbinder lo que este encargo le permite hacer es a través de Attributes en su ViewDataModel describir la validación del atributo (y hacer alusión a la representación de IU). Sin embargo, esto es todo el tiempo de compilación definido, pero sería una gran referencia para comenzar a escribir un encuadernador personalizado.

En su caso, su carpeta de modelo debe obtener la validación de un campo en tiempo de ejecución a partir de un archivo xml/cadena.

Si tiene una ruta como:

routes.MapRoute(null, "Forms/{formName}/", new { action = "Index", controller = "Forms", formName = ""}), 

entonces se podría localizar la forma correcta xml en FormsController.Index(string formName) y pasarlo a la vista.

El FormsModel debe contener todos los métodos posibles para obtener datos. No veo otra forma de evitar esto. El Xml podría asignarse a un nombre de función (posiblemente incluso argumentos) que puede invocar utilizando la reflexión en el FormsModel para completar el ViewData o escribir ViewDataModel con datos.

La vista de índice de formulario podría generar un formulario de ese xml a través de una extensión HtmlHelper que toma un XmlDocument.

Luego, cuando (o asp.net mvc) vincula su formulario a su ViewData se invoca su carpeta de modelo personalizada, puede inspeccionar los valores actuales del controlador para buscar el formName y buscar el xml correspondiente que contiene todas las reglas de validación . El ModelBinder es responsable de completar ModelState con cualquier error definido en el tiempo de ejecución.

Es una tarea difícil, pero cuando se quitó con éxito la pena en mi opinión :)

actualización una mejor alternativa al modelo de datos sería un esquema de base muy floja como sugiere David Liddle. Todavía me tomaría la molestia de guardarlo como xml (o algún otro formato serializado) y utilizarlo para generar la vista y para mantener las reglas de validación para un ModelBinder personalizado, para que tenga más control sobre el diseño y la validación de cada campo.

3

Otra opción es tener un esquema de base de datos muy ligeramente acoplado.

 
//this will contain all the fields and types that the admin user sets 
**ApplicationFields** 
FieldName 
FieldType 
... 

//these are all the values that have some mapping to a ParentObjectID 
**FormValues** 
ParentObjectID 
FieldName 
FieldValue 

Cuando envíe su tiempo de ejecución genera View (de ApplicationFields) a continuación, sólo a través de su bucle FormCollection y tratar y la puso sobre la ParentObject necesita actualizar.

 
public ActionResult MyForm(FormCollection form) 
{ 
    //this is the main object that contains all the fields 
    var parentObject; 

    foreach (string key in form) 
    { 
     parentObject.SetValue(key, form[key]); 
    } 
    ... 

Luego, su ParentObject podría ser algo como esto ... respuesta

 
public partial class ParentObject 
{ 
    IList _FormValues; 

    public void SetValue(string key, string value) 
    { 
     //try and find if this value already exists 
     FormValue v = _FormValues.SingleOrDefault(k => k.Key == key); 

     //if it does just set it 
     if (v != null) 
     { 
      v.Value = value; 
      return; 
     } 

     //else this might be a new form field added and therefore create a new value 
     v = new FormValue 
     { 
      ParentObjectID = this.ID, 
      Key = key, 
      Value = value 
     }; 

     _FormValues.Add(v); 
    } 
} 
12

que tenían la misma necesidad en un proyecto reciente. Creé una biblioteca de clase para esto. Acabo de lanzar una nueva versión de la biblioteca.

Tal vez puede ayudarle a: ASP.NET MVC Dynamic Forms

+0

Al solo mirar el código fuente de esta solución bastante agradable, ¿está pensando en liberar el código fuente para la versión 2.0 en absoluto? Si no, ¿tiene una lista de diferencias entre las dos versiones? – Tr1stan

+0

Se publica la fuente. Todo el proyecto es de código abierto @ codeplex. –

+0

Oh, bien, estaba confundido porque la fecha de lanzamiento de v2 es 2012, pero el código fuente no ha cambiado desde 2010, que fue cuando lanzó v1, eso fue todo. Gracias. – Tr1stan

0

no puedo ver enormes ventajas de la generación X Forms o cualquier otro "abstracción" sobre el HTML comparación con la generación recta hacia adelante de HTML con "Web Forms 2.0" lista de controles para modelos como List<Tuple<Meta, Value>>. Nota: en el lado del servidor, en cualquier caso, estarás obligado a analizar los resultados manualmente para adaptarlos a tus estructuras.

La búsqueda de "próximas abstracciones de capa" es buena para el desarrollo rápido, pero vea, "generar código" (tiempo de ejecución o tiempo de compilación) tiene su propia especificación. Por lo general, el código de generación de "capa inferior" es la mejor solución que generar el código de "capa abstracta más alta".

Así que solo tiene que ir y el código wirte genera Controles Web 2 en el bucle @Foreach.

4

Puede hacerlo fácilmente usando mi biblioteca FormFactory.

Por defecto se refleja en contra de un modelo de vista para producir una matriz PropertyVm[], pero también se puede crear mediante programación las propiedades, por lo que podría cargar la configuración de una base de datos a continuación, crear PropertyVm.

Este es un fragmento de un script de Linqpad.

`` `

//import-package FormFactory 
//import-package FormFactory.RazorGenerator 


void Main() 
{ 
    var properties = new[]{ 
     new PropertyVm(typeof(string), "username"){ 
      DisplayName = "Username", 
      NotOptional = true, 
     }, 
     new PropertyVm(typeof(string), "password"){ 
      DisplayName = "Password", 
      NotOptional = true, 
      GetCustomAttributes =() => new object[]{ new DataTypeAttribute(DataType.Password) } 
     } 
    }; 
    var html = FormFactory.RazorEngine.PropertyRenderExtension.Render(properties, new FormFactory.RazorEngine.RazorTemplateHtmlHelper()); 

    Util.RawHtml(html.ToEncodedString()).Dump(); //Renders html for a username and password field. 
} 

` ``

Hay un demo site con ejemplos de las diversas características puede configurar (por ejemplocolecciones anidadas, autocompletar, fechadores, etc.)

Cuestiones relacionadas