Визуализируйте форму MVC с помощью отражения и Razor Tag Helper. Net 3.1 - PullRequest
0 голосов
/ 27 мая 2020

Я хочу создать сложную форму, которая будет создавать структуру мастера, форму частичных шагов, проверку и отправку. Эта структура должна использовать аннотации атрибутов модели для создания одного объекта структуры поверх модели. Итак, после размышлений у меня есть модель и еще один класс с описанием структуры. Все свойства внутри являются строками с полями, которые я должен передать вспомогательному тегу 'asp -for'. Итак, часть кода:

@foreach(var field in @group.Fields) {
  <div class="col-12 col-md-6 col-lg-4">
     <div class="form-group md-form md-outline">
        <label asp-for="@field.Name" class="control-label"></label>
        <input asp-for="@field.Name" class="form-control" />
        <span asp-validation-for="@field.Name" class="text-danger"></span>
     </div>
  </div>  
}

Это не работает, потому что помощник тега ожидает выражения и генерирует неправильные значения, которых от меня не ожидают. Значение в @ field.Name - «PostAddress.Street1». Если я заменю все «@ field.Name» на «PostAddress.Street1», все будет работать правильно, как я ожидал. Fields render

Кажется, проблема небольшая, но я много пробую и читаю на форумах, но не нашел ответа. Что я пробовал:

  1. Эксперимент 1

Пытался унаследовать класс InputTagHelper от библиотеки do tnet и переопределить свойство For, но безуспешно. Он изменил ModelExpression, но не изменил интерфейс. Может быть, базовый класс имеет лог c, чтобы пропустить этот измененный объект, или неправильно сгенерирован:

[HtmlAttributeName("asp-for")]
    public new ModelExpression For
    {
        get
        {
            return base.For;
        }
        set
        {
            ModelExpression me = value;
            if (value.Model != null)
            {
                var viewData = this.ViewContext.ViewData as ViewDataDictionary<AbnServiceModel>;

                me = ModelExpressionProvider.CreateModelExpression<AbnServiceModel, string>(viewData, model => model.PostAddress.Street1);
            }
            base.For = me;
        }
    }

==================== ============================= 2. Эксперимент 2

Попробуйте получить оригинальную реализацию из. NET Core code и внесли некоторые изменения в код, чтобы исправить проблему. Но код и зависимости с внутренними библиотеками были очень сложными, и я отвергаю эту идею.

Опыт 3 с использованием HTML помощников

@Html.Label(@field.Name, "", new{ @class="control-label" })
@Html.Editor(@field.Name,  new { htmlAttributes = new{ @class="form-control" } })
@Html.ValidationMessage(@field.Name,"",new { htmlAttributes = new{ @class="text-danger" } })

Он правильно отображает компоненты в браузере, но проверка на стороне клиента с использованием jquery .validate.unobtrusive. js не работает. Не знаю почему.

Опыт 4 Использование HTML помощников:

@Html.LabelFor(m=>m.PostAddress.Street1, new{ @class="control-label" })
@Html.EditorFor(m=>m.PostAddress.Street1,  new { htmlAttributes = new{ @class="form-control" } })
@Html.ValidationMessageFor(m=>m.PostAddress.Street1,"",new { htmlAttributes = new{ @class="text-danger" } })

Проверка работает, но класс не применялся должным образом, возможно, это моя ошибка. Но другая проблема здесь в том, что я не использую выражение, которое является строкой, которую можно получить из объекта модели. Также он не улавливает все logi c, которые включены в asp -for помощника тегов.

Эксперимент 5 Пытался создать свой собственный помощник по тегам и использовать генератор для создания содержимого html. Но это означает, что мне нужно реализовать все logi c как помощники в ядре do tnet, чтобы иметь все функции, такие же, как Expiriment 2

Поэтому я не нашел хорошего решения этого "простая" проблема, и потрачено несколько дней на ее исследование и код для ее решения. Я удивлен, что нет возможности передать строковую переменную с именем свойства, и это не сработает.

Может ли кто-нибудь помочь мне решить эту проблему на реальном примере? Я не нашел ответа во всех сообщениях. Я хочу иметь все логики c из asp - для помощника тега, но использовать переменную для передачи выражения. Это может быть сложно, просто хочу иметь какое-то решение, чтобы продолжить мой проект.

Спасибо

1 Ответ

0 голосов
/ 17 июня 2020

Я решил свою проблему. Создан один вспомогательный метод:

public static class CommonHelperMethods
    {
        public static ModelExplorer GetModelExplorer(this ModelExplorer container, string field, IModelMetadataProvider modelMetadataProvider = null)
        {
            ModelExplorer result = container;
            var fields = field.Split(".").ToList();

            var match = Regex.Match(fields[0], @"(.+)\[(\d)+\]");
            if (!match.Success)
            {
                fields.ForEach(x =>
                {
                    result = result?.GetExplorerForProperty(x) ?? result;
                });
            }
            else
            { //List have to create own Property browser
                string proName = match.Groups[1].Value;
                int idx = Convert.ToInt32(match.Groups[2].Value);
                var model = ((IList)result?.GetExplorerForProperty(proName).Model)[idx];
                var targetProperty = model.GetType().GetProperty(fields[1]);
                var targetValueModel = targetProperty.GetValue(model);


                var elementMetadata = modelMetadataProvider.GetMetadataForProperty(model.GetType(), fields[1]);
                return new ModelExplorer(modelMetadataProvider, container, elementMetadata, targetValueModel);
            }    
            return result;
        }
    }

И просто переопределите вспомогательный класс тега следующим образом:

using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.TagHelpers;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace GetTaxSolutions.Web.Infrastructure.TagHelpers
{
    [HtmlTargetElement("input", Attributes = ForAttributeName, TagStructure = TagStructure.WithoutEndTag)]
    public class InputTextGtTaxHelper : InputTagHelper
    {
        private const string ForAttributeName = "asp-for";
        [HtmlAttributeName("not-exp")]
        public bool NotExpression { get; set; } = false;

        [HtmlAttributeName(ForAttributeName)]
        public new ModelExpression For
        {
            get
            {
                return base.For;
            }
            set
            {
                ModelExpression me = value;
                if (NotExpression)
                {
                    var modelExplorertmp = value.ModelExplorer.Container.GetModelExplorer(value.Model.ToString(), ModelMetadataProvider);
                    var modelExplorer = new ModelExplorer(ModelMetadataProvider, value.ModelExplorer.Container, modelExplorertmp.Metadata, modelExplorertmp.Model);
                    me = new ModelExpression(value.Model.ToString(), modelExplorer);
                }
                base.For = me;
            }
        }

        public IModelExpressionProvider ModelExpressionProvider { get; }
        public IModelMetadataProvider ModelMetadataProvider { get; }
        public IActionContextAccessor Accessor { get; }

        public InputTextGtTaxHelper(
            IHtmlGenerator generator,
            IModelExpressionProvider modelExpressionProvider,
            IModelMetadataProvider modelMetaDataProvider) : base(generator)
        {
            ModelExpressionProvider = modelExpressionProvider;
            ModelMetadataProvider = modelMetaDataProvider;
        }
    }
}

Также следует пропустить исходный класс при регистрации вспомогательного тега:

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.InputTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers
@removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.LabelTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers
@removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.ValidationMessageTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers
@removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.SelectTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper GetTaxSolutions.Web.Infrastructure.TagHelpers.*, GetTaxSolutions.Web

И при использовании модели в выражении просто необходимо передать атрибут 'no-exp' входным элементам. В противном случае будет работать как оригинальный помощник тега.

<input not-exp="true" asp-for="@field.Name" class="form-control" />

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

...