Создание динамических форм с помощью.net.core

У меня есть требование иметь разные формы для разных клиентов, которые могут быть настроены в фоновом режиме (в конце концов, в базе данных)

Моя первоначальная идея состоит в том, чтобы создать объект для "Form", который имеет "Словарь FormItem" для описания полей формы.

Затем я могу создать новую динамическую форму, выполнив следующее (это будет из базы данных / службы):

   private Form GetFormData()
    {
        var dict = new Dictionary<string, FormItem>();
        dict.Add("FirstName", new FormItem()
        {
            FieldType = Core.Web.FieldType.TextBox,
            FieldName = "FirstName",
            Label = "FieldFirstNameLabel",
            Value = "FName"
        });
        dict.Add("LastName", new FormItem()
        {
            FieldType = Core.Web.FieldType.TextBox,
            FieldName = "LastName",
            Label = "FieldLastNameLabel",
            Value = "LName"
        });
        dict.Add("Submit", new FormItem()
        {
            FieldType = Core.Web.FieldType.Submit,
            FieldName = "Submit",
            Label = null,
            Value = "Submit"
        });

        var form = new Form()
        {
            Method = "Post",
            Action = "Index",
            FormItems = dict
        };

        return form;
    }

Внутри моего контроллера я могу получить данные формы и передать их в представление

        public IActionResult Index()
    {
        var formSetup = GetFormData(); // This will call into the service and get the form and the values

        return View(formSetup);
    }

Внутри представления я вызываю HtmlHelper для каждого из FormItems

@model Form
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

@using FormsSpike.Core.Web
@{
    ViewData["Title"] = "Home Page";
}

@using (Html.BeginForm(Model.Action, "Home", FormMethod.Post))
{
    foreach (var item in Model.FormItems)
    {
        @Html.FieldFor(item);
    }
}

Затем при повторной публикации мне приходится перебирать переменные формы и сопоставлять их снова. Это кажется очень старой школой, которую я ожидал бы сделать в каком-то модельном переплете.

   [HttpPost]
    public IActionResult Index(IFormCollection form)
    {
        var formSetup = GetFormData();

        foreach (var formitem in form)
        {
            var submittedformItem = formitem;

            if (formSetup.FormItems.Any(w => w.Key == submittedformItem.Key))
            {
                FormItem formItemTemp = formSetup.FormItems.Single(w => w.Key == submittedformItem.Key).Value;
                formItemTemp.Value = submittedformItem.Value;
            }
        }
        return View("Index", formSetup);
    }

Это я могу затем выполнить некоторые сопоставления, которые будут обновлять базу данных в фоновом режиме.

Моя проблема в том, что это просто неправильно:o{

Также я использовал очень простой HtmlHelper, но я не могу использовать стандартные htmlHelpers (такие как LabelFor) для создания форм, так как нет модели для привязки к..

 public static HtmlString FieldFor(this IHtmlHelper html, KeyValuePair<string, FormItem> item)
    {
        string stringformat = "";
        switch (item.Value.FieldType)
        {
            case FieldType.TextBox:
                stringformat = $"<div class='formItem'><label for='item.Key'>{item.Value.Label}</label><input type='text' id='{item.Key}' name='{item.Key}' value='{item.Value.Value}' /></ div >";
                break;
            case FieldType.Number:
                stringformat = $"<div class='formItem'><label for='item.Key'>{item.Value.Label}</label><input type='number' id='{item.Key}' name='{item.Key}' value='{item.Value.Value}' /></ div >";
                break;
            case FieldType.Submit:
                stringformat = $"<input type='submit' name='{item.Key}' value='{item.Value.Value}'>";
                break;
            default:
                break;
        }

        return new HtmlString(stringformat);
    }

Также проверка не будет работать, поскольку атрибуты (например, RequiredAttribute для RegExAttribute) отсутствуют.

У меня неправильный подход к этому или есть более определенный способ заполнить формы, подобные этой?

Есть ли способ создать динамическую ViewModel, которая может быть создана из первоначальной настройки и при этом сохранить все богатство MVC?

3 ответа

Вы можете сделать это, используя мою библиотеку FormFactory.

По умолчанию он отражает модель представления для создания PropertyVm[] массив:

`` `

var vm = new MyFormViewModel
{
    OperatingSystem = "IOS",
    OperatingSystem_choices = new[]{"IOS", "Android",};
};
Html.PropertiesFor(vm).Render(Html);

`` `

но вы также можете создавать свойства программно, чтобы вы могли загрузить настройки из базы данных, а затем создать PropertyVm,

Это фрагмент из скрипта 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.
}

`` `

Это демонстрационный сайт с примерами различных функций, которые вы можете настроить (например, вложенные коллекции, автозаполнение, средства выбора даты и т. Д.)

Я собираюсь разместить здесь свое решение, так как я нашел этот поиск «как создать динамическую форму в ядре mvc». Я не хотел использовать стороннюю библиотеку.

Модель:

      public class IndexViewModel
{
    public Dictionary<int, DetailTemplateItem> FormBody { get; set; }
    public string EmailAddress { get; set; }
    public string templateName { get; set; }
}

cshtml

      <form asp-action="ProcessResultsDetails" asp-controller="home" method="post">
    <div class="form-group">
        <label asp-for=@Model.EmailAddress class="control-label"></label>
        <input asp-for=@Model.EmailAddress class="form-control" />
    </div>
    @foreach (var key in Model.FormBody.Keys)
    {
        <div class="form-group">

            <label asp-for="@Model.FormBody[key].Name" class="control-label">@Model.FormBody[key].Name</label>
            <input asp-for="@Model.FormBody[key].Value" class="form-control" value="@Model.FormBody[key].Value"/>
            <input type="hidden" asp-for="@Model.FormBody[key].Name"/>
        </div>
    }
    <input type="hidden" asp-for="templateName" />
    <div class="form-group">
        <input type="submit" value="Save" class="btn btn-primary" />
    </div>
</form>

Вы можете использовать JJMasterData, он может создавать динамические формы из ваших таблиц во время выполнения или во время компиляции. Поддерживает как .NET 6, так и .NET Framework 4.8.

  1. После настройки пакета откройте /en-us/DataDictionary в своем браузере.
  2. Создайте словарь данных, добавив имя таблицы
  3. Нажмите «Дополнительно», «Получить сценарии», «Выполнить хранимые процедуры», а затем нажмите «Предварительный просмотр» и проверьте.
  4. Чтобы использовать CRUD во время выполнения, перейдите по адресу en-us/MasterData/Form/Render/{YOUR_DICTIONARY}.
  5. Чтобы использовать CRUD на определенной странице или настроить во время компиляции, следуйте приведенному ниже примеру:

На вашем контроллере:

          public IActionResult Index(string dictionaryName)
    {
        var form = new JJFormView("YourDataDictionary");
        
        form.FormElement.Title = "Example of compile time customization"
        
        var runtimeField = new FormElementField();
        runtimeField.Label = "Field Label";
        runtimeField.Name = "FieldName";
        runtimeField.DataType = FieldType.Text;
        runtimeField.VisibleExpression = "exp:{pagestate}='INSERT'";
        runtimeField.Component = FormComponent.Text;
        runtimeField.DataBehavior = FieldBehavior.Virtual; //Virtual means the field does not exist in the database.
        runtimeField.CssClass = "col-sm-4";

        form.FormElement.Fields.Add(runtimeField);

        return View(form);
    }

На ваш взгляд:

      @using JJMasterData.Web.Extensions
@model JJFormView

@using (Html.BeginForm())
{
    @Model.GetHtmlString()
}
Другие вопросы по тегам