{"id":11146,"date":"2024-12-22T12:46:08","date_gmt":"2024-12-22T12:46:08","guid":{"rendered":"https:\/\/putridparrot.com\/blog\/?p=11146"},"modified":"2024-12-22T12:47:31","modified_gmt":"2024-12-22T12:47:31","slug":"blazor-templates","status":"publish","type":"post","link":"https:\/\/putridparrot.com\/blog\/blazor-templates\/","title":{"rendered":"Blazor templates"},"content":{"rendered":"<p>Way back I wrote the post <a href=\"https:\/\/putridparrot.com\/blog\/blazor-components\/\" target=\"_blank\">Blazor Components<\/a>. This post demonstrated how to create simple components within Blazor. I didn&#8217;t progress massively, using Blazor back when that post was written, but I&#8217;ve been getting back into Blazor recently.<\/p>\n<p>Let&#8217;s look at a powerful feature of Blazor (a little bit like WPF lookless controls) in that we can create Blazor templates using code behind with entry points (placeholders) for the UI to interact with.<\/p>\n<p>One of the obvious uses of such a template might be for lists or grids etc. as these might allow us to add a header and\/or footer along with rows which have the same look and feel but with different data.<\/p>\n<p>Let&#8217;s look at a very simple example, a ListView template which allows us to output a list of rows where the list (at a top level) can be styled by the code using it and each row and be styled as well. Ofcourse you could style things quite easily if you know the CSS class etc. but here I mean styled as in you can change the UI itself, hence let&#8217;s begin by looking at how we could change a ListView to use ordered lists or unordered lists.<\/p>\n<p><em>Note: I&#8217;m going to reuse the weather forecast data supplied when creating a Blazor WebAssembly application in Visual Studio<\/em><\/p>\n<p><strong>The component<\/strong><\/p>\n<p>First off, let&#8217;s create a component named <em>ListView<\/em>, I&#8217;m creating it within a folder named <em>Components<\/em>. The ListView.razor look like this<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n@typeparam TItem\r\n\r\n@if (ListTemplate is not null)\r\n{\r\n    @ListTemplate(\r\n        @:@{foreach (var item in Items) \r\n            {\r\n                @ItemTemplate(item)\r\n            }\r\n          }\r\n    )\r\n}\r\n<\/pre>\n<p>We&#8217;re declaring a typeparam named TItem as this adds generic type parameter support which you&#8217;ll see being used in the code behind file. We can add a constraint to this type if required, but for this example we&#8217;re not too concerned about what type TItem takes. In the example above we&#8217;re assuming the user will supply the ListTemplate otherwise nothing is displayed, ofcourse we could create a default output if we wished or display a message for the developer.<\/p>\n<p>The code behind, the ListView.razor.cs looks like this<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\nusing Microsoft.AspNetCore.Components;\r\n\r\nnamespace BlazorTemplates.Components;\r\n\r\npublic partial class ListView&lt;TItem&gt;\r\n{\r\n    &#x5B;Parameter] public RenderFragment&lt;RenderFragment&gt;? ListTemplate { get; set; }\r\n    &#x5B;Parameter] public RenderFragment&lt;TItem&gt; ItemTemplate { get; set; } = default!;\r\n    &#x5B;Parameter] public IReadOnlyList&lt;TItem&gt; Items { get; set; } = default!;\r\n}\r\n<\/pre>\n<p>What&#8217;s happening here is that we&#8217;re declaring a ListTemplate which is a RenderFragment, which is simple put, a piece of UI content, the generic type is also a RenderFragment &#8211; so ListTemplate is UI content of UI content essentially. This will act as our &#8220;outer&#8221; UI, so for example, if were using the ListView for ordered list items, then this would contain the &lt;ol&gt;&lt;\/ot&gt; section of the UI. <\/p>\n<p>The ItemTemplate is again a RenderFragment, hence UI content supplied by the calling code, but this type it takes a generic TItem type, this refers to the type of the list items themselves, i.e. for the WeatherForeast sample code, this is a WeatherForecast class\/type. Hence the ItemTemplate will be passed an item from the supplied Items list (within our template) and allows the calling code to render each item.<\/p>\n<p>The Items property is what&#8217;s used by the calling code to supply the list of data to our template.<\/p>\n<p><strong>Calling the component<\/strong><\/p>\n<p>If we look again at the .razor code you&#8217;ll see that really all we&#8217;re doing it calling the ListTemplate and passing through a segment of code using Wig-Pig syntax which creates a RanderFragment. This then gets used to call through to the ItemTemplate and this template is passed each item from the list of Items so that we can render each row, let&#8217;s see the code in use<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n&lt;ListView Items=&quot;_forecasts&quot;&gt;\r\n  &lt;ListTemplate Context=&quot;rows&quot;&gt;\r\n    &lt;ul&gt;@rows&lt;\/ul&gt;\r\n  &lt;\/ListTemplate&gt;\r\n  &lt;ItemTemplate Context=&quot;forecast&quot;&gt;\r\n    &lt;li&gt;@forecast.Summary&lt;\/li&gt;\r\n  &lt;\/ItemTemplate&gt;\r\n&lt;\/ListView&gt;\r\n<\/pre>\n<p><em>Note: _forecasts is the same as the Weather page in the default Blazor WebAssembly template, hence an array of WeatherForeast items<\/em><\/p>\n<p>In the example above we&#8217;re renaming the context for use within each template, we could have just used the following if we preferred<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n&lt;ListView Items=&quot;_forecasts&quot;&gt;\r\n  &lt;ListTemplate&gt;\r\n    &lt;ul&gt;@context&lt;\/ul&gt;\r\n  &lt;\/ListTemplate&gt;\r\n  &lt;ItemTemplate&gt;\r\n    &lt;li&gt;@context.Summary&lt;\/li&gt;\r\n  &lt;\/ItemTemplate&gt;\r\n&lt;\/ListView&gt;\r\n<\/pre>\n<p>As can be seen (which ever version you use) then ListTemplate is creating the &#8220;outer&#8221; element, then passing the context into the RenderFragement we supplied within the ListView.razor file, which itself then loops through each item with the Items list (which was defined on the ListView Items property in the above code.<\/p>\n<p>The template then calls the ItemTemplate for each item and the template for this is supplied in the ItemTemplate fragment in the above code. In this case we using li and displaying the summary for each weather forecast.<\/p>\n<p><strong>Using the template if different scenarios<\/strong><\/p>\n<p>Now using the template to display an unordered list is great, and yes it&#8217;s obvious how we can easily change the ol to an ul but I&#8217;m sure this looks like a lot of effort for something very easy in Blazor anyway. So let&#8217;s look at extending this into something a little more useful, but before we do that, here&#8217;s the example of using this template with ordered lists, unordered lists and tables<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n&lt;ListView Items=&quot;_forecasts&quot;&gt;\r\n  &lt;ListTemplate&gt;\r\n    &lt;ul&gt;@context&lt;\/ul&gt;\r\n  &lt;\/ListTemplate&gt;\r\n  &lt;ItemTemplate&gt;\r\n    &lt;li&gt;@context.Summary&lt;\/li&gt;\r\n  &lt;\/ItemTemplate&gt;\r\n&lt;\/ListView&gt;\r\n\r\n&lt;ListView Items=&quot;_forecasts&quot;&gt;\r\n  &lt;ListTemplate&gt;\r\n    &lt;ol&gt;@context&lt;\/ol&gt;\r\n  &lt;\/ListTemplate&gt;\r\n  &lt;ItemTemplate&gt;\r\n    &lt;li&gt;@context.Summary&lt;\/li&gt;\r\n  &lt;\/ItemTemplate&gt;\r\n&lt;\/ListView&gt;\r\n\r\n&lt;ListView Items=&quot;_forecasts&quot;&gt;\r\n  &lt;ListTemplate&gt;\r\n    &lt;table&gt;@context&lt;\/table&gt;\r\n  &lt;\/ListTemplate&gt;\r\n  &lt;ItemTemplate&gt;\r\n    &lt;tr&gt;@context.Summary&lt;\/tr&gt;\r\n  &lt;\/ItemTemplate&gt;\r\n&lt;\/ListView&gt;\r\n<\/pre>\n<p>How about we extend our template to allow for a header and a footer, hence allowing us to use more of the table functionality but also have the same functionality now for the ordered and unordered lists.<\/p>\n<p>We can simply add the following to our ListView.razor.cs class<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n&#x5B;Parameter] public RenderFragment? HeaderTemplate { get; set; }\r\n&#x5B;Parameter] public RenderFragment? FooterTemplate { get; set; }\r\n<\/pre>\n<p>and within the ListView.razor file just add HeaderTemplate and FooterTemplate like this<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n@typeparam TItem\r\n\r\n@if (ListTemplate is not null)\r\n{\r\n    @HeaderTemplate\r\n\r\n    @ListTemplate(\r\n        @:@{foreach (var item in Items) \r\n            {\r\n                @ItemTemplate(item)\r\n            }\r\n          }\r\n    )\r\n\r\n    @FooterTemplate\r\n}\r\n<\/pre>\n<p>All that&#8217;s left to do is add HeaderTemplate code to our calling code, so for example the table based version would look like this (I&#8217;ve added more table like element\/attribute parts to this code)<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n&lt;ListView Items=&quot;_forecasts&quot;&gt;\r\n  &lt;HeaderTemplate&gt;\r\n    &lt;thead&gt;&lt;tr&gt;&lt;th scope=&quot;col&quot;&gt;Summary&lt;\/th&gt;&lt;\/tr&gt;&lt;\/thead&gt;\r\n  &lt;\/HeaderTemplate&gt;\r\n  &lt;FooterTemplate&gt;\r\n    &lt;tfoot&gt;&lt;tr&gt;&lt;th scope=&quot;row&quot;&gt;Footer&lt;\/th&gt;&lt;\/tr&gt;&lt;\/tfoot&gt;\r\n  &lt;\/FooterTemplate&gt;\r\n  &lt;ListTemplate&gt;\r\n    &lt;table&gt;\r\n      &lt;caption&gt;This is a table&lt;\/caption&gt;\r\n      &lt;tbody&gt;@context&lt;\/tbody&gt;\r\n     &lt;\/table&gt;\r\n  &lt;\/ListTemplate&gt;\r\n  &lt;ItemTemplate&gt;\r\n    &lt;tr scope=&quot;row&quot;&gt;@context.Summary&lt;\/tr&gt;\r\n  &lt;\/ItemTemplate&gt;\r\n&lt;\/ListView&gt;\r\n<\/pre>\n<p>Now we can also add a header and footer to our ordered and unordered lists in the same way, i.e.<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n&lt;ListView Items=&quot;_forecasts&quot;&gt;\r\n  &lt;HeaderTemplate&gt;\r\n    &lt;div&gt;Summary&lt;\/div&gt;\r\n  &lt;\/HeaderTemplate&gt;\r\n  &lt;FooterTemplate&gt;\r\n    &lt;div&gt;Footer&lt;\/div&gt;\r\n  &lt;\/FooterTemplate&gt;\r\n  &lt;ListTemplate&gt;\r\n    &lt;ul&gt;@context&lt;\/ul&gt;\r\n  &lt;\/ListTemplate&gt;\r\n  &lt;ItemTemplate&gt;\r\n    &lt;li&gt;@context.Summary&lt;\/li&gt;\r\n   &lt;\/ItemTemplate&gt;\r\n&lt;\/ListView&gt;\r\n<\/pre>\n<p><em>Note: ofcourse this is not very pretty, so I&#8217;ll leave it to the reader to create some nice CSS for the header and footer divs<\/em><\/p>\n<p>That&#8217;s pretty much it &#8211; templates can be useful if you&#8217;re going to abstract some UI pattern in a reusable way.<\/p>\n<p>Code for this post is available on <a href=\"https:\/\/github.com\/putridparrot\/blog-projects\/tree\/master\/BlazorTemplates\" target=\"_blank\">GitHub<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Way back I wrote the post Blazor Components. This post demonstrated how to create simple components within Blazor. I didn&#8217;t progress massively, using Blazor back when that post was written, but I&#8217;ve been getting back into Blazor recently. Let&#8217;s look at a powerful feature of Blazor (a little bit like WPF lookless controls) in that [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[305],"tags":[],"class_list":["post-11146","post","type-post","status-publish","format-standard","hentry","category-blazor"],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/11146","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/comments?post=11146"}],"version-history":[{"count":3,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/11146\/revisions"}],"predecessor-version":[{"id":11149,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/11146\/revisions\/11149"}],"wp:attachment":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/media?parent=11146"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/categories?post=11146"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/tags?post=11146"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}