To View Component or To Not View Component (that i...
# help-with-umbraco
j
Hej! Actually doing some Umbraco 13 work and wanted to sense check my use of View Components. Example: - Rootpage with multiple Tracks (listed as "Singles", because music init) - Want to display details of each Track in a card - Want to move the code for each of these cards into a dedicated component, passing the Track instance as an parameter My general approach would be to make a View Component (
TrackCardViewComponent.cs
) which returns the code for the card, using input of
@model Track
. I've included the code at the end of this message. This works just fine - I more or less want to confirm this is the correct approach, or if I'm over-complicating things. My crucial reason for using the View Component was to pass the Track instance as an parameter to the card, but that might be possible via other means. All comments/advice/direction appreciated! Rich Rootpage.cshtml:
Copy code
<div class="row row-cols-3 p-0 m-0">
     @foreach (Track track in Model.Singles)
     {
         @await Component.InvokeAsync("TrackCard", track)
     }
 </div>
TrackCardViewComponent.cs:
Copy code
public class TrackCardViewComponent : ViewComponent
{
    public IViewComponentResult Invoke(Track track)
    {
        return View("~/Views/ViewComponents/TrackCard.cshtml", track);
    }
}
TrackCard.cshtml:
Copy code
@model Track

@if (Model.TrackArtwork is null) return;
<div class="col-sm-6 col-md-4 p-2">
    <img class="w-100" src="@Model.TrackArtwork.MediaUrl()" />
    <p>@Model.Name | @Model.TrackArtwork.MediaUrl()</p>
    <p>Test</p>
</div>
b
Hey Rich - not sure your code snippet is right there, as you seem to be iterating through
Model.Singles
in the TrackCard partial? That should just have the code block inside the foreach. Otherwise the approach seems fine, my rule of thumb is I use a ViewComponent where I have some custom logic or need a custom ViewModel sent to the partial for whatever reason. If it's just Umbraco content/ModelsBuilder models (e.g.
Track
is a native content/element type already) there is no need to add the complexity of a ViewComponent and I'll just do it in the razor code directly.
n
Hey Richard, so for me using the View Component doesn't add anything to your workflow you could just as easily call the Partial view directly passing in the Track as the model from your main view. From a View Component perspective, if you were maniplating the data, calling other services etc then that would be a better use case. For example if you passed in the track, then queried another service for extended details such as play lists that track is on, then it would make more sense.
j
Since your viewComponent isn't doing anything you could call the partial directly:
Copy code
csharp

@Html.PartialAsync("~/Views/ViewComponents/TrackCard.cshtml", track)
j
There's two slightly slightly separate questions to consider, design and technical/implementation. RE the technical side of things: Most seasoned Umbraco devs will tell you "Just use a partial". It's what we're used to, it's straightforward, and it gets the job done. However, Microsoft want to move people more towards ViewComponents because they allow for better encapsulation, do away with some of Razor's weirdness, and better represent how modern websites are "componentized" by design - it means writing/maintaining more code though. The design question is, does it need spinning out into a separate file at all? i.e. are you going to use a "TrackCard" in more than one place while retaining (mostly) the same logic and HTML? If you have designs that have already componentized a TrackCard then great - make a dedicated component for it (either partial or VC) but if you don't, or don't know yet if you will then there's nothing wrong just dropping it in the main template and spinning it out later when you have a better idea of how it will be reused. Avoid Hasty Abstractions.
j
Firstly, thank you to everyone for your answers! Let me try and go through them: @Barry Fogarty: ah I think I confused matters by including the screenshot with no description or context (and was only included by accident). I've removed that.
TrackCard.cshtml
partial is being called from
Rootpage.cshtml
foreach track in Model.Singles.
@Nik / @Jemayn: thanks for this, I was unsure on how you call Partials while including the Model. Greatly appreciate those examples, that clarifies (my implementation felt like it was too much, which is honestly why I posted)
@Jason: hugely insightful, thanks for that - so let's review both questions raised: - Technical: I posted to get an understanding of, at least, the different options available (i.e. Partials vs ViewComponents). Greatly appreciate the additional/historical context - Componentalise All The Things. - Design: Yep I completely agree with this, and the pitfalls with premature engineering. In this instance the specific answer is "Yes the same component will be reused elsewhere", so I feel confident making the card into a component.
j
Insightful take, thanks @Jason ! The hasty abstractions bit is something I see a lot and had trouble clearly explaining my issue with 🙂
m
Sort of related... If it's only simple id passing into a partial you can also pass a view data dictionary @Html.PartialAsync("~/Views/ViewComponents/TrackCard.cshtml", track, vdd) or tag helper 😉 (works out Async or not for you) easiest is to extend the current ViewData
var vdd = new ViewDataDictionary(ViewData){{"uid", "[GUID]"}}
(but watch for key collisions) but you can also pass a new empty
var vdd = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary()) { { "noContainer", true } };
(but watch for model rehydration from modelstate) Might also be nice to put the views in
/views/partials/components/
can now see in the backoffice and also viewcomponents will natively look for views in that folder without the absolute view path. https://www.reddit.com/r/dotnet/comments/1960qyx/a_guide_to_different_ways_of_passing_data_to/
j
This is exactly the kind of Razor weirdness that I'm talking about BTW 😅
m
😉
Though not sure I agree on the leave it all in a template for later..
makes templates unwieldy...
think it's valid to use partials for encapsulation of a single chunk of html...
... // @RenderBody() //
j
That's very different to OP's use case though.
The nature of the abstraction of Header and Footer will not change.
And it's mainly there for DX.
m
oh thought you were saying leave
@foreach(track){big chunk of html)
in the template. sorry..
j
Oh, I was.
Well,
@foreach(track){small chunk of html}
m
it's never small when the frontender gets hold of it and insists on BEM 😉
Whilst we are talking viewcomponents.. I get some wierd model rehydration issues.. when
return CurrentUmbracoPage();
the inputs get rehydrated but not the model... not sure if that's me misunderstanding net core.. or umbraco + partial + viewcomponent... (rehydration seems to happen via the modelstate.. to put input values back but the model remains as if new model())
j
Alas, I've not needed/tried to use CurrentUmbracoPage() with ViewComponents yet.
31 Views