asp.net-mvc - programacion - tag helpers asp net core
Formularios dinámicos(generados en tiempo de ejecución) en ASP.NET MVC (6)
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?
Aquí está la situación:
- Un administrador del sitio puede definir parámetros de formulario (campos, tipo de campos, validación) con una GUI (vista MVC).
- Según sea necesario, el tiempo de ejecución genera el formulario para el usuario final según 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í.
- El usuario final completa el formulario, envía los resultados, 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 lo permitiría. Sobre todo, solo necesito que el administrador pueda especificar campos adicionales como cuadros de texto, casillas de verificación, botones de radio y cuadros combinados. También necesitaré la aplicación para asignar un espacio para que estos datos se guarden en la base de datos, pero creo que ya tengo esa parte resuelta.
Gracias por la ayuda.
La respuesta de cottsak es muy atractiva.
Hay al menos dos motores XForms del lado del cliente. Aquí hay uno:
No puedo ver las grandes ventajas de generar XForms o cualquier otra "abstracción" sobre HTML comparando con la generación directa de HTML con la lista de controles de "Web Forms 2.0" para modelos como List<Tuple<Meta, Value>>
. Nota: en el lado del servidor, en cualquier caso, estaría obligado a analizar los resultados manualmente para adaptarlos a sus estructuras.
Buscar "abstracciones de la siguiente capa" es bueno para un desarrollo rápido, pero vea, "generar código" (tiempo de ejecución o tiempo de construcción) tiene su propio específico. 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 superior".
Así que simplemente vaya y conecte el código que genera los controles Web 2 en el ciclo @Foreach.
Otra opción es tener un esquema de base de datos muy débilmente 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 Vista generada en tiempo de ejecución (desde ApplicationFields), simplemente realice un bucle a través de su FormCollection e intente establecerla en el ParentObject que 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]); } ...
Entonces tu parentObject podría ser algo como esto ...
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); } }
Puedes hacerlo muy fácilmente usando mi biblioteca de FormFactory .
De forma predeterminada, se refleja en un modelo de vista para producir una matriz PropertyVm[]
, pero también puede crear las propiedades mediante programación, por lo que puede cargar la configuración desde una base de datos y luego crear PropertyVm
.
Este es un fragmento de un script 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 sitio de demostración con ejemplos de las diversas características que puede configurar (por ejemplo, colecciones anidadas, autocompletar, seleccionadores de fechas, etc.)
Tuve la misma necesidad en un proyecto reciente. He creado una biblioteca de clase para esto. Acabo de lanzar una nueva versión de la biblioteca.
Tal vez pueda ayudarte: ASP.NET MVC Dynamic Forms
Una forma de hacer esto es crear su propio ModelBinder
que estaría en el corazón de sus formularios generados. Un modelbinder es responsable de validar ModelState
y reconstruir ModelState
escrito (asumiendo que sus vistas están escritas).
El DataAnnotations modelos de DataAnnotations podría ser una buena referencia para esto. Lo que este modelbinder personalizado le permite hacer es a través de los Attributes
en su ViewDataModel
describe la validación del atributo (y la sugerencia de la representación de la interfaz de usuario). Sin embargo, todo esto es tiempo de compilación definido, pero sería una gran referencia para comenzar a escribir un encuadernador de modelos personalizado.
En su caso, su carpeta de modelos debería obtener la validación de un campo en tiempo de ejecución de un archivo / cadena xml.
Si tienes una ruta como:
routes.MapRoute(null, "Forms/{formName}/", new { action = "Index", controller = "Forms", formName = ""}),
Luego, puede localizar el formulario correcto xml en FormsController.Index(string formName)
y pasarlo a la vista.
El FormsModel
debe contener todos los métodos posibles para obtener datos que no veo de ninguna otra manera al respecto. El Xml podría asignarse a un nombre de función (posiblemente incluso a argumentos) que puede invocar utilizando la reflexión en FormsModel
para llenar los FormsModel
de FormsModel
los FormsModel
de FormsModel
con FormsModel
.
La vista de Form Index podría generar un formulario desde ese xml a través de una extensión HtmlHelper
que toma un XmlDocument
.
Luego, cuando usted (o asp.net mvc) vincula su formulario con su ViewData
se invoca su carpeta de modelos 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 llenar ModelState
con cualquier error definido en el tiempo de ejecución.
Es una tarea difícil, pero cuando la arranco con éxito bien vale la pena en mi opinión :)
Actualizar una mejor alternativa a los datos del modelo sería un esquema de base de datos muy vago como sugiere David Liddle. Todavía me tomaría la molestia de guardarlo como xml (o algún otro formato serializado) y usarlo para generar la vista y 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.