In XAML it is quite easy to use datatemplate on items.
In Razor, it does not seem possible to do it without including an external view/component.
It is quite often that I get several foreach on a page, with the same kind of items, and using razor, I have to repeat the same template several times, it is not very handy.
I would see something similar to functions:
@templates {
EventItemTemplate(string name, DateTime time, string place) {
<div class="event">
<div class="event-name">@name</div>
<div class="event-time">@time.ToString("g")</div>
<div class="event-place">@place</div>
<div>
}
}
<div class="events">
<div class="events-title">Coming events and in progress</div>
@foreach(var e in events.Where(e2=>e2.EndTime > DateTime.UtcNow))
{
@EventItemTemplate(e.Name, e.Start, e.Place)
}
</div>
<div class="events">
<div class="events-title">Past events</div>
@foreach(var e in events.Where(e2=>e2.EndTime < DateTime.UtcNow))
{
@EventItemTemplate(e.Name, e.Start, e.Place)
}
</div>
It would highly simplify the display of a structure like this:
building.cs
public class Building
{
public string Address {get;set;}
public List<Floor> Floors {get;set;}
}
public class Floor
{
public string Number {get;set;}
public List<Premise> Premises {get;set;}
}
public class Premise
{
public string Door {get;set;}
public string Owner {get;set;}
public List<string> Residents {get;set;}
}
building.cshtml
@templates {
BuildingTemplate(Building b) {
<div class="building">
<div class="building-name">@b.Name</div>
<div class="building-floors">
@foreach(var f in p.Floors)
{
@FloorTemplate(f)
}
</div>
<div>
}
FloorTemplate(Floor f) {
<div class="floor">
<div class="floor-number">@f.Number</div>
<div class="floor-premises">
@foreach(var p in f.Premises)
{
@PremiseTemplate(p)
}
</div>
<div>
}
PremiseTemplate(Premise p) {
<div class="premise">
<div class="premise-door">@p.Door</div>
<div class="premise-owner">@p.Owner</div>
<div class="premise-residents">
@foreach(var r in p.Resident)
{
<span class="premise-resident">@r</span>
}
</div>
<div>
}
}
<div class="buildings left-side">
@foreach(var b in Model.BuildingsLeft)
{
@BuildingTemplate(b)
}
</div>
<div class="buildings right-side">
@foreach(var b in Model.BuildingsRight)
{
@BuildingTemplate(b)
}
</div>
It may allow recursive templates
@templates {
CommentItemTemplate(CommentViewModel comment) {
<div class="comment">
<div class="comment-name">By: @name</div>
<div class="comment-time">@time.ToString("g")</div>
@if(comment.Comments?.Count>0)
{
<div class="comment-subcomments">
@foreach(var c in comment.Comments)
{
@CommentItemTemplate(c)
}
</div>
}
<div>
}
}
<div class="comments">
<div class="comments-title">Comments</div>
@foreach(var c in Model.Comments)
{
@CommentItemTemplate(c)
}
</div>
In that specific case, component may be a better choice of implementation, but that is just an example.
Globally defined c# variables may be available or not, depending on language constraints. If not available, they can still be passed as parameters anyway.
It seems quite clean to me, does it conflict the current language in any way?
Thanks for contacting us, @JeanCollas.
This is definitely something interesting and we do plan to provide some easy reusability mechanism of Razor markup. At the moment, those plans are just being shaped, so we'll keep this issue around to track.
That would be really helpful in many cases!
So great to avoid code-duplication or avoid multiplication of sub-views and corresponding models for very basic (and local) templates.
Hey I found this issue searching for the same problem. So to be clear is there currently a way to re-use (or declare in some central location) component templates?
@legistek Component templates are RenderFragments, which you could create centrally and reuse.
Hey @danroth27 I can see how you would do it in C# code by making a lambda/delegate. But I should have specified that I'd like to declare them in the cshtml markup, yet not directly inside the component that's using it. I couldn't see any of the tutorials or examples showing how to do that. Is that possible now?
Basically I want to do the equivalent of a WPF DataTemplate defined as a StaticResource.
@legistek Right, we don't have a fully comparable data template experience to what WPF has. I was simply pointing out what we have available today.
Thanks no worries, the code method is a useful workaround for sure.
I should also point out, it's easy enough to create a component for each "reusable" data template, give it a DataContext parameter of your own, and then declare it as the only element in your actual component template.
@legistek It is not as explicit as in-view DataTemplating, but you may create a partial view in include it using in the view
@foreach(var p in ps)
{
@await Html.PartialAsync("/MyViewPath/_MyPartialView.cshtml", new { Param1 = p.p1, Param2 = p.p2, Param3=p.p3})
}
_MyPartialView.cshtml:
@model dynamic
<div>
<div><span>Param 1: </span><span>@Model.param1</span></div>
<div><span>Param 2: </span><span>@Model.param2</span></div>
<div><span>Param 3: </span><span>@Model.param3</span></div>
</div>
We do now support something very much like this: https://github.com/dotnet/AspNetCore.Docs/blob/1e199f340780f407a685695e6c4d953f173fa891/aspnetcore/blazor/webassembly-performance-best-practices.md#defining-reusable-renderfragments-in-code
This issue has been resolved and has not had any activity for 1 day. It will be closed for housekeeping purposes.
See our Issue Management Policies for more information.
Most helpful comment
Thanks for contacting us, @JeanCollas.
This is definitely something interesting and we do plan to provide some easy reusability mechanism of Razor markup. At the moment, those plans are just being shaped, so we'll keep this issue around to track.