Передать ModelExpression из помощника по тегам в частичное представление - PullRequest
1 голос
/ 01 апреля 2019

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

<mydateinput for="@Model.StartDate" />

Внутри c # кода помощника по тегам я бы определил свойство for, и из того, что я могу сказать, это должно быть определено как "ModelExpression".

public class MyDateInputTagHelper : TagHelper
{
    public ModelExpression For { get; set; }
    ...
}

Используя код, упомянутый ранее в этой статье , я отрисовываю частичное представление и просто передаю класс помощника по тегам в качестве модели для частичного представления.

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        base.Process(context, output);

        ((IViewContextAware)HtmlHelper).Contextualize(ViewContext);

        output.Content.SetHtmlContent(HtmlHelper.Partial("~/Views/Partials/TagHelpers/MyDateInput.cshtml", this));

    }

Наконец, мое частичное представление определено примерно так

<input asp-for="For" />

Проблема, с которой я сталкиваюсь, заключается в том, что я не могу заставить выражение модели «Для» правильно перейти в частичное представление. Когда я просматриваю источник HTML, я просто вижу буквально имя «For» в атрибутах id и name входных данных. Кроме того, значение, установленное в моей модели страницы бритвы, также отображается некорректно.

Я хочу, чтобы html отображался таким образом, чтобы при публикации страницы модель на моей странице бритвы заполнялась значениями, выбранными под тегом помощник / частичное представление. В частности, это будет вокруг «StartDate» (в моем примере), а не свойства «For».

Кто-нибудь имеет представление о том, что я делаю неправильно, и что я могу изменить в этом примере, чтобы правильно передать выражение ModelExpression в частичное представление?

1 Ответ

3 голосов
/ 02 апреля 2019

Невозможно сделать это с InputTagHelper напрямую. Для получения дополнительной информации см. Бритва / проблемы # 926 , а также аналогичный вопрос по SO .

A Walkaournd

Но, как обходной путь, вы могли бы использовать пользовательский HtmlHelper<TModel> (как и @Html) для достижения той же цели . Ваш частичный вид может выглядеть так:

@model App.TagHelpers.PartialVM

@{ 
    var PartialHtml = Model.HtmlHelper;
}

@PartialHtml.Label(Model.NewFor.Name,Model.NewFor.Name) 
@PartialHtml.TextBox(Model.NewFor.Name, Model.NewModel)

А именно, используйте @Html.Label() & @Html.TextBot вместо <label> & <input>.

Здесь PartialVM - это простой класс, который содержит метаинформацию о модели:

public class PartialVM
{

    public PartialVM(ModelExpression originalFor, IHtmlHelper htmlHelper)
    {
        var originalExplorer = originalFor.ModelExplorer;

        OriginalFor = originalFor;
        OriginalExplorer = originalExplorer;
        NewModel = originalExplorer.Model;
        NewModelExplorer = originalExplorer.GetExplorerForModel(NewModel);
        NewFor = new ModelExpression(OriginalFor.Name, NewModelExplorer);
        this.HtmlHelper = htmlHelper;
    }


    public IHtmlHelper HtmlHelper { get; set; }

    public ModelExpression OriginalFor { get; set; }
    public ModelExplorer OriginalExplorer { get; set; }
    public ModelExpression NewFor { get; set; }
    public ModelExplorer NewModelExplorer { get; set; }
    public Object NewModel { get; set; }
}

Обратите внимание, что IHtmlHelper на самом деле IHtmlHelper<TSomeDynamicModel> вместо простого IHtmlHelper.

Наконец, измените TagHelper, как показано ниже:

    [HtmlTargetElement("my-custom-input")]
    public class MyCustomInputTagHelper : TagHelper
    {
        private readonly IServiceProvider _sp;

        [ViewContext]
        public ViewContext ViewContext { set; get; }

        public ModelExpression For { get; set; }

        public MyCustomInputTagHelper(IServiceProvider sp)
        {
            this._sp = sp;
        }

        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            base.Process(context, output);

            var originExplorer = For.ModelExplorer;
            var newModel = originExplorer.Model;
            var newExplorer = originExplorer.GetExplorerForModel(newModel);
            var newFor = new ModelExpression(For.Name, newExplorer);
            var ModelType = originExplorer.Container.Model.GetType();

            var htmlHelperType = typeof(IHtmlHelper<>).MakeGenericType(ModelType);
            var htmlHelper = this._sp.GetService(htmlHelperType) as IHtmlHelper;   // get the actual IHtmlHelper<TModel>
            (htmlHelper as IViewContextAware).Contextualize(ViewContext);

            var vm = new PartialVM(For, htmlHelper);

            var writer = new StringWriter();
            var content = await htmlHelper.PartialAsync("~/Views/Partials/TagHelpers/MyDateInput.cshtml", vm);
            output.TagName = "div";
            output.TagMode = TagMode.StartTagAndEndTag;
            output.Content.SetHtmlContent(content);
        }
    }

Теперь вы можете передать любую asp-for строку выражения для свойства For вашего TagHelper, и оно должно работать как положено.

Контрольный пример:

Предположим, у нас есть модель Dto:

public class XModel {
    public int Id { get; set; }
    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }
    public string ActiveStatus{ get; set; }
}

Вы можете сделать это следующим образом:

/// the action method looks like:
///    var model = new XModel {
///        StartDate = DateTime.Now,
///        EndDate = DateTime.Now.AddYears(1),
///        ActiveStatus = "Active",
///    };
///     return View(model);


@model XModel

<my-custom-input For="StartDate" />
<my-custom-input For="EndDate" />
<my-custom-input For="ActiveStatus" />

Вот скриншот, когда он рендерит:

enter image description here

...