diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/HomeController.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/HomeController.cs index 00e2a17fc1b..69ce76ff4b4 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/HomeController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/HomeController.cs @@ -104,13 +104,13 @@ public async Task DisplayShape(string contentItemId) return NotFound(); } - var shape = Shape - .Foo() - .Line(contentItem.As().Line); + var shape = await Shape.Foo(Line: contentItem.As().Line); return View(shape); } + public IActionResult AddProperty() => View(); + public ActionResult Raw() { return View(); diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Views/EmbedContentInShape.cshtml b/src/OrchardCore.Modules/OrchardCore.Demo/Views/EmbedContentInShape.cshtml new file mode 100644 index 00000000000..f054a9d781d --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Views/EmbedContentInShape.cshtml @@ -0,0 +1,21 @@ +@using Microsoft.AspNetCore.Html + +
+

The content is inserted below this line.

+

@Model.HtmlContent

+

The content is inserted above this line.

+ + @if (Model.SomeProperty != null) + { +

And here is another property: @Model.SomeProperty.

+ } + + @if (Model.OtherContent is IHtmlContent otherContent) + { +
+

Another property called "OtherContent" starts here.

+

@otherContent

+

Another property called "OtherContent" ends here.

+
+ } +
diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Views/Home/AddProperty.cshtml b/src/OrchardCore.Modules/OrchardCore.Demo/Views/Home/AddProperty.cshtml new file mode 100644 index 00000000000..58a71d9f4b2 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Views/Home/AddProperty.cshtml @@ -0,0 +1,23 @@ +@using Microsoft.AspNetCore.Html + + + + The inner HTML of the <add-property name="propertyName"> tag helper (which is a direct child + of the <shape> tag helper) is converted into HTML and then passed to the shape as a model + property. The property's name is the string passed into the name attribute. + + Even other shapes can be included! + + + You can have multiple, they are just @nameof(IHtmlContent) type shape properties. + + + + + + + If a <add-property> tag helper exists, it takes precedence over any matching attribute of the + <shape> tag helper. This is because child tag helpers are evaluated after the shape is + created, right before it's displayed. + + diff --git a/src/OrchardCore/OrchardCore.DisplayManagement/TagHelpers/AddPropertyTagHelper.cs b/src/OrchardCore/OrchardCore.DisplayManagement/TagHelpers/AddPropertyTagHelper.cs new file mode 100644 index 00000000000..1af4565a368 --- /dev/null +++ b/src/OrchardCore/OrchardCore.DisplayManagement/TagHelpers/AddPropertyTagHelper.cs @@ -0,0 +1,28 @@ +using Microsoft.AspNetCore.Html; +using Microsoft.AspNetCore.Razor.TagHelpers; +using System.Threading.Tasks; + +namespace OrchardCore.DisplayManagement.TagHelpers; + +[HtmlTargetElement("add-property", Attributes = "name", TagStructure = TagStructure.NormalOrSelfClosing)] +public class AddPropertyTagHelper : TagHelper +{ + [HtmlAttributeName("name")] + public string Name { get; set; } + + public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) + { + if (string.IsNullOrWhiteSpace(Name)) + { + return; + } + + var content = await output.GetChildContentAsync(useCachedResult: false); + var shape = (IShape)context.Items[typeof(IShape)]; + shape.Properties[Name.Trim()] = output.Attributes.ContainsName("value") + ? output.Attributes["value"].Value + : new HtmlString(content.GetContent()); + + output.SuppressOutput(); + } +} diff --git a/src/OrchardCore/OrchardCore.DisplayManagement/TagHelpers/BaseShapeTagHelper.cs b/src/OrchardCore/OrchardCore.DisplayManagement/TagHelpers/BaseShapeTagHelper.cs index 9c191c1014d..7a7a6f95e7e 100644 --- a/src/OrchardCore/OrchardCore.DisplayManagement/TagHelpers/BaseShapeTagHelper.cs +++ b/src/OrchardCore/OrchardCore.DisplayManagement/TagHelpers/BaseShapeTagHelper.cs @@ -133,7 +133,7 @@ public override async Task ProcessAsync(TagHelperContext tagHelperContext, TagHe shape.Metadata.Wrappers.Add(Convert.ToString(output.Attributes["wrapper"].Value)); } - tagHelperContext.Items.Add(typeof(IShape), shape); + tagHelperContext.Items[typeof(IShape)] = shape; if (!string.IsNullOrWhiteSpace(Cache)) { diff --git a/src/docs/reference/core/Placement/README.md b/src/docs/reference/core/Placement/README.md index f216d5418dc..f5c416bf1e2 100644 --- a/src/docs/reference/core/Placement/README.md +++ b/src/docs/reference/core/Placement/README.md @@ -175,6 +175,26 @@ Metadata tag helper example: ``` +#### Adding properties with additional tag helpers + +Properties can be passed to a shape by adding attributes to the shape tag helper, as mentioned above. But you can also use the `` tag helper inside ``. This even lets you pass Razor code as properties with the `IHtmlContent` value, if you omit the `value` attribute. Something that can't be easily done otherwise. + +```xml + + + + +

Some complicated HTML

+
+ You can even include shapes: + +
+
+
+``` + +This is the same as `` where you'd have to construct `someHtmlContentVariable` separately. Of course, you can mix and match the different formats, for example, to only use `` when you want to pass HTML content as property. + ### Date Time shapes #### `DateTime` diff --git a/src/docs/releases/2.0.0.md b/src/docs/releases/2.0.0.md index a85e9a8865c..18a5b5b9377 100644 --- a/src/docs/releases/2.0.0.md +++ b/src/docs/releases/2.0.0.md @@ -620,6 +620,10 @@ A new filter named `supported_cultures` was added to allow you to get a list of ``` +### Adding properties with additional tag helpers + +The new `` tag helper can be placed inside the `` tag helpers to add properties to the shape. This is similar to `prop-*` attributes, but you can also include Razor code as the `IHtmlContent` property value, which was impossible before. See more details [here](../reference/core/Placement/README.md#adding-properties-with-additional-tag-helpers). + ### Sealing Types Many type commonly used by modules can be `sealed`, which improves runtime performance. While it's not mandatory, we recommend that you consider applying this improvement to your own extensions as well. We've implemented this enhancement in the following pull-requests: