Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Add a <partial /> Tag Helper #5916

Closed
DamianEdwards opened this issue Mar 7, 2017 · 8 comments
Closed

Add a <partial /> Tag Helper #5916

DamianEdwards opened this issue Mar 7, 2017 · 8 comments

Comments

@DamianEdwards
Copy link
Member

DamianEdwards commented Mar 7, 2017

Right now, there are four methods on IHtmlHelper for rendering partials, and it isn't clear to users if one is better than the other. RenderPartialAsync is certainly much harder to call and looks much uglier at the call site.

@Html.Partial("_AnalyticsBody")
@await  Html.PartialAsync("_AnalyticsBody")
@{ Html.RenderPartial("_AnalyticsBody"); }
@{ await Html.RenderPartialAsync("_AnalyticsBody"); }

All of these methods also have an overload that accepts a model object, e.g. @await Html.PartialAsync("_AnalyticsBody", SomeModel)

It would be much nicer to have a Tag Helper for partial rendering that just does "the right thing":

<partial name="_AnalyticsBody" model="SomeModel" />

Strawman implementation:

namespace Microsoft.AspNetCore.Mvc.Razor.TagHelpers
{
    [HtmlTargetElement("partial", Attributes = "name")]
    public class PartialTagHelper : TagHelper
    {
        private readonly IHtmlHelper _htmlHelper;

        public PartialTagHelper(IHtmlHelper htmlHelper)
        {
            _htmlHelper = htmlHelper;
        }

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

        public string Name { get; set; }

        public object Model { get; set; }

        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            ((IViewContextAware)_htmlHelper).Contextualize(ViewContext);

            // Determine best way to do this, e.g. calling RenderPartialCoreAsync() instead passing a TextWriter that does the right thing if that's actually better, handle null Model, etc.
            var content = await _htmlHelper.PartialAsync(Name, Model);
            
            output.TagName = null;
            output.Content.SetHtmlContent(content);
        }
    }
}

@NTaylorMullen @rynowak @pranavkm @Eilon

@grahamehorner
Copy link

grahamehorner commented Mar 8, 2017

I like the partial TagHelper idea 👍 would love to be able to pass a predicate/action the would be evaluated at runtime to either render the partial or not; eg.

<partial name="userProfilePartial" predicate="namespace.staticClass.predictionOrAction">

@Eilon Eilon removed this from the 2.0.0-preview1 milestone Mar 8, 2017
@Eilon
Copy link
Member

Eilon commented Mar 8, 2017

Moving out of the milestone just so that we pick it up in triage. But yeah this looks good.

@mrtristan
Copy link

mrtristan commented Apr 5, 2017

this is very similar to the body of work that i'm up against right now with a little bit of a difference in capability... i'm looking to meld the concepts of a tag helper (having content within it in the html itself) with the concepts of sections and partials.

perhaps i'm way too far out there for what you guys are talking about but consider the potential for the following:

<bootstrap-modal>
    <some-form />
    <some-button />
</bootstrap-modal>

(or as follows as you're describing:)

<partial name="_BootstrapModal">
    <partial name="_SomeForm" model="SomeFormModel" />
    <partial name="_SomeButton" model="SomeButtonModel" />
</partial>

where the big idea here is that each of these TagHelpers has the inherent capability of loading some partial as you're describing, but also the ability to inject the template that it was wrapped around, in this case the two partial TagHelpers within the bootstrap-modal

the 'trick' here is in how you dictate to the bootstrap-modal where it should inject the template that it wrapped (thus the mention of sections above... similar kind of idea.)

what do you all think about that?

@eduardoluizm
Copy link

Why not:

[HtmlTargetElement("partial", Attributes = "name")]
    public class PartialTagHelper : TagHelper
    {
        private readonly IHtmlHelper _htmlHelper;

        public PartialTagHelper(IHtmlHelper htmlHelper)
        {
            _htmlHelper = htmlHelper;
        }

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

        public string Name { get; set; }

        [HtmlAttributeName("asp-for")]
        public ModelExpression For { get; set; }

        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            ((IViewContextAware)_htmlHelper).Contextualize(ViewContext);
            ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix = For.Name;

            var content = await _htmlHelper.PartialAsync(Name, For.Model);

            output.TagName = null;
            output.Content.SetHtmlContent(content);
        }
    }

TemplateInfo.HtmlFieldPrefix can keep a full name if I render an input on partial view.

@dougbu
Copy link
Member

dougbu commented Sep 7, 2017

See the Tag Helper Pack and #6512.

@danroth27 danroth27 added this to the 2.1.0 milestone Nov 1, 2017
@pranavkm pranavkm self-assigned this Nov 29, 2017
pranavkm added a commit that referenced this issue Nov 29, 2017
pranavkm added a commit that referenced this issue Nov 29, 2017
pranavkm added a commit that referenced this issue Nov 29, 2017
pranavkm added a commit that referenced this issue Nov 30, 2017
@grahamehorner
Copy link

Q: shouldn’t Async methods have an optional CancellationToken with the default of default(CancellationToken.None) ?

@pranavkm
Copy link
Contributor

pranavkm commented Dec 1, 2017

@grahamehorner I'm not sure what the context of your question is? Is this about the missing CancellationToken parameter on ProcessAsync?

@grahamehorner
Copy link

look at source of a number of TagHelper the ProcessAsync doesn't seem to take a cancellation token; which I would have expected to see to allow clear up on the TagHelper when the pipeline/caller is cancelled.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

8 participants