We recently had a discussion in the office about the point of using a view model in Episerver, what should go in it and why it's needed. A lot of the team had very conflicting views, so I thought I'd share my views. For a quick MVC overview recap, in MVC you have a View which contains your HTML (CSS, js, etc..) in a .cshtml file, a model (a page type or block type definition) and a controller that defines how the model will be passed to the view, who can access it, how it should be cached, etc... and a ViewModel.
What is the point of the ViewModel?
One definition of what the ViewModel should be used for is a filter between the properties defined in the pages and blocks properties and what is exposed to the front-end view. For Episerver that means never exposing the Page Type of Block Type definition directly but adding properties for the things that will be displayed. In my opinion, this just seems like a lot of boilerplate code to write, especially if you don't have anything security-wise to warrant all the effort. So if we're not going to use this definition of what a view model is... what are we?
When I usually design a site, I'll create a base ViewModel for pages and blocks. An example of what that base view model class would look like is shown below:
The ViewModel code would look like this:
The code to return the ViewModel to a view would look like this:
Following this approach, you have the underlying page and block properties exposed to your view. So apart from forwarding on the page or block property what should go in there?
Friendly Url/Link Resolving
In a lot of pages/blocks we need to link to other internal pages within the website, however, when we render a pages LinkUrl property it renders out the internal Url which is useless for your SEO. The View Model is the perfect place to add a link resolver to display the friendly Url.
I haven't included the code for the link resolver in this article, but if you want to get yourself a copy have a look at my Dependencies article.
As we can have multiple views for a block, we want to keep all logic out of the view to stick to the DRY principle, so the next best place to keep it is within the ViewModel. When you start integrating your HTML, you will undoubtedly hit situations where you need to change the class based on a certain criteria, for example you may have an editor option to Hide a heading on a block:
This snippet will render a "none" class if the user has enabled it, otherwise, nothing will be displayed and the heading will display. If you have multiple views per control this will reduce duplicate code in each view. Doing this also allows you to unit test that the right class is displayed in the right situation.
Concatenating Multiple Elements Into A Single Property
Let's say you have a block with a Title, First Name and Surname property. Within the view model you can create a FullName method to save you having the concatenation code in your view.
Image Alt Tags
To have good SEO you need to have Alt tags but sometimes an editor may not add one. In the Viewmodel you enforce, the Alt tag has a value.
Rendering Custom HTML Elements
You may also want to render your own custom Html tag. The following tag will render out a different heading tag like based on a selection factory defined in the block, for example:
Filtering Content Area Contents
When you have a block like a carousel, you will likely have a number of child blocks, usually within a content area defined on the parent. You can use the View Model to iterate through those items and return them in list or even DTO/Poco to make the rendering code easier.
By now I'm hoping you're starting to see the point of the View Model. It's the place to keep code that isn't part of a page or block type definition and having to add any sort of logic within the view. Following this approach means you should keep your Episerver page definitions free of code and your views free of code, maintaining a good layer between presentation and business logic.