Ответ 1
У меня была такая же потребность в недавнем проекте. Для этого я создал библиотеку классов. Я только что выпустил новую версию библиотеки.
Может быть, это может вам помочь: Динамические формы ASP.NET MVC
Это общий вопрос дизайна: Как реализовать динамическую (сгенерированную во время выполнения) форму в ASP.NET MVC?
Здесь ситуация:
Настройка не требует поддержки вложенных элементов управления, сторонних элементов управления и т.д., но я подозреваю, что для этого очень элегантный дизайн. В основном, мне просто нужно, чтобы администратор мог указать дополнительные поля в виде текстовых полей, флажков, переключателей и списков со списком. Мне также понадобится приложение, чтобы выделить пространство для хранения этих данных в db, но я считаю, что эта часть была выяснена.
Спасибо за помощь.
У меня была такая же потребность в недавнем проекте. Для этого я создал библиотеку классов. Я только что выпустил новую версию библиотеки.
Может быть, это может вам помочь: Динамические формы ASP.NET MVC
Вы можете сделать это очень легко, используя мою библиотеку FormFactory.
По умолчанию он отражает модель представления для создания массива PropertyVm[]
, но вы также можете создавать свойства программно, чтобы вы могли загружать настройки из базы данных, а затем создавать PropertyVm
.
Это фрагмент из Linqpad script.
`` `
//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.
}
`` `
Theres демонстрационный сайт с примерами различных функций, которые вы можете настроить (например, вложенные коллекции, автозаполнение, датапикеры и т.д.)
Другой вариант - иметь очень слабо связанную схему базы данных.
//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
Когда вы отправляете свой Сгенерированный просмотр (из ApplicationFields), просто пропустите свой FormCollection и попробуйте установить его в ParentObject, который вам нужно обновить.
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]); } ...
Тогда ваш объект parentObject может быть чем-то вроде этого...
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); } }
Один из способов сделать это - создать свой собственный ModelBinder
, который будет в основе ваших сгенерированных форм. Модельный блок отвечает за проверку ModelState
и восстановление типизированного ViewDataModel
(при условии ввода ваших просмотров).
DataAnnotations связывание модели может быть хорошей ссылкой для этого, что позволяет этот настраиваемый modelbinder с помощью Attributes
на вашем ViewDataModel
описать проверку атрибута (и намек на визуализацию пользовательского интерфейса). Однако это все определенное время компиляции, но было бы отличной ссылкой, чтобы начать писать пользовательский bindbinder.
В вашем случае ваше связующее устройство должно получить подтверждение для поля во время выполнения из файла/строки xml.
Если у вас есть маршрут, например:
routes.MapRoute(null, "Forms/{formName}/", new { action = "Index", controller = "Forms", formName = ""}),
Затем вы можете найти правильную форму xml в FormsController.Index(string formName)
и передать ее в представление.
FormsModel
должен содержать все возможные методы для получения данных. Я не вижу другого способа обойти это. Xml может сопоставлять имя функции (возможно, даже аргументы), которое вы можете вызвать с помощью отражения на FormsModel
, чтобы заполнить данные ViewData
или напечатанные ViewDataModel
.
Вид для индекса формы может генерировать форму из этого xml через расширение HtmlHelper
, которое принимает XmlDocument
.
Затем, когда вы (или asp.net mvc) свяжете свою форму с вашим ViewData
, вызывается ваше настраиваемое связующее устройство, оно может проверять текущие значения контроллера для поиска formName и искать соответствующий xml, который содержит всю проверку правила. Затем ModelBinder
отвечает за заполнение ModelState
любыми ошибками, определенными во время выполнения.
Это трудная задача, но когда она удалена успешно, стоит ее на мой взгляд:)
Обновить лучшая альтернатива данным модели будет очень свободной схемой базы данных, как предлагает Дэвид Лиддл. Я по-прежнему не в состоянии сохранить его как xml (или некоторый сериализованный формат) и использовать его для создания представления и для хранения правил проверки для пользовательского ModelBinder
, чтобы у вас было больше контроля над макетом и валидацией каждого поля.
cottsak answer очень привлекателен.
Есть как минимум два клиентских XForms-движка. Здесь один:
Я не вижу огромных преимуществ генерации XForms или любой другой абстракции по сравнению с HTML по сравнению с прямым форматированием HTML с помощью списка управления "Web Forms 2.0" для модели типа List<Tuple<Meta, Value>>
. Примечание: на стороне сервера вам в любом случае придется вручную анализировать результаты, чтобы они соответствовали вашим структурам.
Поиск "абстракций следующего слоя" хорош для быстрой разработки, но см. "генерировать код" (время выполнения или время сборки) имеет свою специфику. Обычно генерирующий код "нижнего слоя" является лучшим решением, чем создание кода "более высокого абстрактного слоя".
Итак, просто введите код wirte, который генерирует элементы управления Web 2 в цикле @Foreach.