-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make it possible to add Razor HTML content to the shape as a property #14867
Make it possible to add Razor HTML content to the shape as a property #14867
Conversation
For some reason the original code kept crashing, saying something along the lines of IShape doesn't have "Line" method.
Could you please bring this up on the Thursday meeting? |
Not sure what value it would add to have shape tag with content? why not just use the content in div? It's defeat the purpose of ShapeTagHelper. ShapeTagHelper helps to render a given shape harvested from any module or app- but itself is not a Shape. |
Could you elaborate on what you mean by "just use the content in div"? Is there already an intuitive way to pass Razor HTML content into a shape? If so, I couldn't find it so there may be a discoverability problem. What prompted this contribution: I wanted to make a reusable carousel shape, where the shape contains the frame and the script, but the content is customized by the invoking code. This would save a ton of boilerplate for this use-case. Something like this: Carousel.cshtml: <style asp-name="@ResourceNames.SlickTheme"></style>
<script asp-name="@ResourceNames.Slick" at="Foot"></script>
<div id="@Model.CarouselId" class="carousel__wrapper">
<div class="carousel">
@Model.Content
</div>
<div class="carousel__arrows"></div>
</div>
<script at="Foot" depends-on="@ResourceNames.Slick">
jQuery(function ($) {
$("#@Model.CarouselId").slick({
// parameters to init Slick
});
});
</slick> Some page that wants to display carousels: <h2>@T["Product Highlights"]</h2>
<shape type="Carousel" prop-CarouselId="productCarousel">
@foreach (var shape in productShapes)
{
<div class="carousel__item productCarousel__item">
@await DisplayAsync(shape)
</div>
}
</shape>
<h2>@T["Gallery"]</h2>
<shape type="Carousel" prop-CarouselId="galeryCarousel">
@foreach (var (url, altText) in images)
{
<div class="carousel__item galeryCarousel__item">
<img src="@url" alt="@altText" />
</div>
}
</shape> As you can see, passing the content into the shape is very versatile. It would be much more complicated to achieve this if I had to manually construct the |
In shape |
That's not related to what I'm trying to do. I just want to pass HTML content to the shape. |
You can define any property to your shape as per your need, but should not change the purpose of the ShapeTagHelper. ShapeTagHelper is not shape or content itself it helps render the shape define by the type Define your content in Carousel.cshtml or in another shape that you can call from Carousel.cshtml |
You have already said this and I don't understand why, or why you think anything in this PR changes the purpose of the ShapeTagHelper. Can you please elaborate on this complaint? It just adds another way to pass in an
I don't have to do that. I can replicate my above sample using existing features already, it's just extremely clunky: @{
var productCarouselInner = new HtmlContentBuilder();
foreach (var shape in productShapes)
{
productCarouselInner
.AppendHtml("<div class=\"carousel__item productCarousel__item\">")
.AppendHtml(await DisplayAsync(shape))
.AppendHtml("</div>");
}
var galleryCarouselInner = new HtmlContentBuilder();
foreach (var (url, altText) in images)
{
galleryCarouselInner
.AppendHtml("<div class=\"carousel__item galleryCarousel__item\">")
.AppendHtml($"<img src=\"{ url }\" alt=\"").Append(altText).AppendHtml("\" />")
.AppendHtml("</div>");
}
}
<h2>@T["Product Highlights"]</h2>
<shape type="Carousel" prop-CarouselId="productCarousel" prop-HtmlContent="@productCarouselInner"></shape>
<h2>@T["Gallery"]</h2>
<shape type="Carousel" prop-CarouselId="galeryCarousel" prop-HtmlContent="@galleryCarouselInner"></shape> Of course this way I can't use tag helpers, have to be careful about sanitizing HTML, and the IDE doesn't provide nice Razor syntax highlighting. So in a sense my PR is primarily a usability and tooling improvement. Again, it does not change what the shape tag helper is, it only adds an additional, backwards compatible, non-intrusive way to define a specific property. |
I understand your need, but you are using ShapeTagHelper as Slots. Shapes are not same as slots. Help me understand What happens to the slots content when alternates/wrappers are added to the Shape? In your code example, the slot content you are adding, to me it's just a wrappers of your product/gallery shape. You can add wrapper to your shape to achieve same without modifying ShapeTagHelper. Btw, You can achieve same by using display driver. Which is more elegant and extensible- support wrappers and alternates. Following some pseudo logic of driver that you could use. public override IDisplayResult Display(CarouselModel model)
{
var listShapes = new List<IDisplayResult>();
foreach (var item in model.Products)
{
var ps = Dynamic("CarouselProduct", shape =>
{
shape.Product = item;
})
.Location("Detail", "Content");
listShapes.Add(ps):
}
foreach (var galleryItem in model.Images)
{
var gs = Dynamic("CarouselGallery", shape =>
{
shape.GalleryItem = galleryItem;
})
.Location("Detail", "Gallery");
listShapes.Add(gs):
}
return Combine(listShapes);
} |
This isn't real slots, it just uses the same syntax because it's intuitive. Again, it just passes compiled HTML as a shape parameter, because there is currently no way to pass in HTML Razor into the shape. By the time the shape receives this content it's already built according to the current context and "frozen" so it's just another parameter. Also it's not trying to replace drivers. I agree that if I wanted to make a generic carousel feature I would write a driver and model. But the carousel was just the latest example where this would've came in handy. The point is to use this if you want to create some project-specific reusable elements with an HTML parameter that won't have to be fleshed out for every possible scenario. In other words, it's for the same situations where you use ad-hoc shapes anyway. Can we take a step back? I don't understand what's the harm you see in this PR. Why are you so opposed to modifying ShapeTagHelper? It's backwards-compatible and non-breaking. |
Separation of concern. ShapeTagHelper helps to render a given shape, it is not a shape itself and it doesn't own a shape. Hence ShapeTagHelper should not add/update arbitrary property to a shape type being rendered. Using ShapeTagHelper You can pass properties values to shape using prop-* |
Wait, so your problem is only that the property name is fixed? Because otherwise there is no difference between this and prop-*, it's only syntax. |
I don't have any issue with the feature or need, the only concern is the current proposed solution is not good way to solve the problem |
There is. - in prop-* ShapeTagHelper is not owning the properties being added. It's just adding what's provided as input, on behalf of the caller. |
What do you mean by "owning"? If you look at the code here, you can see that it literally just adds an extra property. In every sense except syntax this is the same as a prop-* attribute |
I think better solution is to add a new child TagHelper of Let's say call it So proposed Template would look like below. <shape type="Carousel" prop-CarouselId="productCarousel">
<slot-content prop-name="MyProp">
@foreach (var shape in productShapes)
{
<div class="carousel__item productCarousel__item">
@await DisplayAsync(shape)
</div>
}
</slot-content>
</shape>
|
We could add a virtual method like
Ok I will look into it... |
…in derived classes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you.
I'm asking this the third time, so please reply: Did you bring it up during the Thursday meeting? Or even the Tuesday one would be good.
afair I did not bring this up in a meeting. |
I will try to bring this tomorrow if I attend in case Sara can't, also you can contact Gabor coz he is almost the time there |
We had something called "capture" in O1 for this I believe, and there is a liquid tag called |
Is this an example of the capture you mentioned? @using (Capture(Layout.Messages)) {
<div id="save-message" class="message message-Warning" style="display:none">@T("You need to hit \"Save\" in order to save your changes.")</div>
<div id="start-message" class="message message-Warning" style="display:none">@T("The workflow needs at least one activity to be set as a starting state.")</div>
} This feels more similar to the <zone name="Messages">
<div id="save-message" class="message message-Warning" style="display:none">@T["You need to hit \"Save\" in order to save your changes."]</div>
<div id="start-message" class="message message-Warning" style="display:none">@T["The workflow needs at least one activity to be set as a starting state."]</div>
</zone> |
@sarahelsaig Correct. Different than your suggestion? |
Yes, |
@sebastienros this is waiting for your reply. |
This pull request has merge conflicts. Please resolve those before requesting a review. |
This pull request has merge conflicts. Please resolve those before requesting a review. |
@sebastienros please reply here. I'll merge it on Wednesday if there's no further feedback. Also, @sarahelsaig, please fix the merge conflict. I know, it feels pointless when the PR is just sitting there, but now it won't :). |
I will update the branch and then merge, no worries :) |
I had a timer saved for this that went off half an hour ago, so would've merged now, but OK then :D. |
I'm trying to avoid making PRs take so long, that's why I need to revise my PRs one more time |
With this change, you can pass HTML content as shape properties using the
<add-property name="propertyName">
child tag helper. The rendered inner HTML will be assigned to the givenpropertyName
. This can be used to easily include complex HTML content in a reusable shape.To test, enable the Orchard Demo module and go to
~/OrchardCore.Demo/Home/AddProperty
. It will look like this:which corresponds to this code:
(EDIT: updated description to represent the current state of the PR. That is using a child tag helper instead of directly using the inner HTML, so doesn't conflict with the existing
<metadata>
tag helper and multiple can be used.)