I have created indexing for block grid items so they are automatically numbered based on various user choices.
I am currently passing the values from items.cshtml to other view using viewdata.
I would like a more robust method.
I have investigated consuming the block grid model into a new view and injecting a new model into my current views but neither seems a good option and over complicated.
AI is suggesting:
1. Extend the Block Grid Model
By creating a custom model that inherits from BlockGridItem, you can add your custom properties while keeping the existing inheritance structure intact. This approach doesn’t require changing the @inherits directive in your views.
public class CustomBlockGridItem : BlockGridItem
{
public int Index { get; set; }
// Add other custom properties here
}
2. Use Composition
Creating a new class that contains the BlockGridItem and your custom properties allows you to keep the existing inheritance structure. You can pass this composed model to your views without changing the @inherits directive.
public class CustomBlockGridItem
{
public BlockGridItem BlockGridItem { get; set; }
public int Index { get; set; }
// Add other custom properties here
}
3. Use a Custom ViewModel
Creating a custom ViewModel that includes the BlockGridItem and your custom properties allows you to pass the ViewModel to your views without changing the @inherits directive.
public class CustomBlockGridViewModel
{
public BlockGridItem BlockGridItem { get; set; }
public int Index { get; set; }
// Add other custom properties here
}
The thing to be aware of with each of these is the scope and lifespan of the object you are working with.
For example a page model created due to a request from visitor A could also be served to visitor B from a cache. If logic was added to inject user data into the model for visitor A that could leak over to the content visitor B sees.
Altering the models for pages with visitor specific data has created problems before, so I would advise caution and some thorough testing with blocks too.
Harry Gordon, wrote a really helpful article on this, which would be worth checking out!
Thanks @mttblss - I actually read Harry’s article at the time and found it very interesting.
In my case the data I am generating is intended for all Backoffice and front end users.
I am simply numbering block items either all in an area or by type in an area e.g. Figure 1. Image 1 etc…
I see Dean, so adding to the model would be safe in this case…
Now there’s a danger I’ve misunderstood again mate, but here goes anyway:
If you’re just applying incremental numbers to items like figures, could you render them all the same in the back end and use CSS counters? The figure captions on this page are done that way
Here’s the CSS, it saves a whole raft of back end pre-processing:
Thanks Matt, CSS counters is where I started and yes they are really great.
But I then began to add more conditional logic in the c#.
It could be used to add classes or data-attributes to control CSS counters of course but this felt like duplication.
Also, I find myself needing to pass other block grid variables to views which I have usually done with viewData.
Over the weekend I got it working using a viewmodel and defaultcontroller but wasn’t entirely happy.
I’m have an idea to add an empty property to the model using the backoffofice (it could also be used by the user as a custom value) but I’d still be interested in learning the best way to add values to models.
I’ve always tried to find another way of handling this type of problem if at all possible. As you said in the original post, each of the possible approaches feels over complicated.
I don’t know there is a “right” answer, Once any performance issues had been ruled out I would fall back to the question which will be easiest to maintain
If I come back to this in a year which approach would I pick up quickest?
If someone else picked up the code which would be most standard and obvious to understand?
In our cshtml for the grid, we then “taste” the View to see if it has an component and then either invokes the ViewComponent or the View
var componentName = block.Content.ContentType.Alias.CapitalizeFirstLetter();
var isComponentAvailable = ViewComponentSelector.SelectComponent(componentName) != null;
if (isComponentAvailable)
@await Component.InvokeAsync(block.Content.ContentType.Alias.CapitalizeFirstLetter(), block)
else await Html.RenderPartialAsync(partialViewName, block);
I know we are just looking to add an index here, but maybe it will be relevant for people who are looking to add -more- stuff to their model or logic to their blocks before they are rendered.
Also final note - my Knowit.Umbraco.InstantBlockPreview supports previewing ViewComponents