【问题标题】:Pass ModelExpression from a Tag Helper to a Partial View将 ModelExpression 从 Tag Helper 传递到局部视图
【发布时间】:2019-03-31 23:40:45
【问题描述】:

我正在使用示例found here 来允许从标签助手中呈现部分视图。我在这里要做的是能够像这样定义一个标签助手:

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

在标签助手的 c# 代码中,我定义了“for”属性,据我所知,这需要定义为“ModelExpression”。

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

使用前面this article 中提到的代码,我正在渲染一个局部视图,并简单地将标签助手的类作为局部视图的模型传递。

    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" />

我遇到的问题是我无法让模型表达式“For”正确传递到局部视图。当我查看 html 源代码时,我只是在输入的 id 和 name 属性中看到字面上的名称“For”。此外,我的剃须刀页面模型中设置的值也没有正确显示。

我想要发生的是 html 将以这样一种方式呈现,即在发布页面时,我的剃须刀页面的模型将填充在标签助手/部分视图下选择的值。它特别是围绕“StartDate”(在我的示例中),而不是属性“For”。

有没有人知道我做错了什么,以及我可以在这个示例中进行哪些更改以正确地将 ModelExpression 传递到局部视图?

【问题讨论】:

    标签: c# asp.net-core asp.net-core-mvc razor-pages asp.net-core-tag-helpers


    【解决方案1】:

    直接使用InputTagHelper 是不可能的。有关详细信息,请参阅 Razor/issues #926a similar question on SO

    解决方法

    但作为一种解决方法,您可以使用自定义HtmlHelper&lt;TModel&gt;(就像@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 而不是&lt;label&gt; & &lt;input&gt;

    这里的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&lt;TSomeDynamicModel&gt;,而不是一个普通的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);
            }
        }
    

    现在您可以为您的TagHelperFor 属性传递任何asp-for 表达式字符串,它应该可以按预期工作。

    测试用例:

    假设我们有一个 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" />
    

    这是渲染时的屏幕截图:

    【讨论】:

    • 很棒的答案,感谢您抽出宝贵时间回答并让我选择继续前进!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多