#To View Component or To Not View Component (that is the forum post)

1 messages ยท Page 1 of 1 (latest)

fluid horizon
#

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:

 <div class="row row-cols-3 p-0 m-0">
     @foreach (Track track in Model.Singles)
     {
         @await Component.InvokeAsync("TrackCard", track)
     }
 </div>

TrackCardViewComponent.cs:

public class TrackCardViewComponent : ViewComponent
{
    public IViewComponentResult Invoke(Track track)
    {
        return View("~/Views/ViewComponents/TrackCard.cshtml", track);
    }
}

TrackCard.cshtml:

@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>
real swift
#

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.

muted obsidian
#

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.

vague widget
#

Since your viewComponent isn't doing anything you could call the partial directly:


@Html.PartialAsync("~/Views/ViewComponents/TrackCard.cshtml", track)

wary nymph
#

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.

fluid horizon
#

Firstly, thank you to everyone for your answers! Let me try and go through them:

@real swift: 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.

#

@muted obsidian / @vague widget: 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)

#

@wary nymph: 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.
vague widget
scenic torrent
#

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)
<partial name="~/Views/ViewComponents/TrackCard.cshtml" model="Model" view-data="vdd" />

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/

wary nymph
scenic torrent
#

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...

#

...
<body>
<partial name="Header"/>
//
@RenderBody()
//
<partial name="Footer"/>
</body>

wary nymph
#

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.

scenic torrent
#

oh thought you were saying leave @foreach(track){big chunk of html) in the template. sorry..

wary nymph
#

Well, @foreach(track){small chunk of html}

scenic torrent
#

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())