One of the unwritten rules in MVC is that ViewModel classes should be kept very simple. They should basically represent data to be displayed in your views. Then, depending on the concrete implementation and the requirements, the whole mapping and connecting it all together part does not always stay simple.
The usage of ViewModels in DD4T 2.0 can actually be very simple, as the example code will try to show. Anything with a simple basis should be also extendable in an easy way and that is the main goal of the framework.
My colleague Quirijn has posted a series of articles about various facets of ViewModels already (see http://blog.trivident.com/viewmodels-in-dd4t-2-0). In this article I will bring it all together to provide a complete, step-by-step example of a ViewModels implementation in DD4T 2.0.
The full Visual Studio solution with the code used in this post can be found on Bitbucket.
All models should implement IViewModel interface. This can also be achieved indirectly by inheriting ViewModelBase class.
Components (or rather, component presentations) are mapped to ViewModels annotated with ContentModel attribute, as was explained in previous posts.
In this example only one property is mapped in both classes for simplicity reasons.
Note that the classes also implement IRenderableViewModel and have RenderData property. This is important for routing and more will be explained later.
Pages are mapped to ViewModels annotated with PageViewModel attribute. The attribute accepts the page template title as a property.
In order to get all component presentations from the page, a property should be annotated with ComponentPresentations attribute. The property name is not important and it can be anything. The only important thing is the property type which should be a concrete implementation of IList (more precisely IList<IViewModel>). In this case it is List<IRenderableViewModel>.
There are a few more attributes available for mapping component presentations, like PresentationsByView and PresentationsByRegion, which filter components presentations by view and region name respectively. Both of these filters can be defined in the component template metadata.
It is also possible to retrieve fields from a page metadata schema in the same way it can be done for component metadata fields, as shown in the example.
It is highly likely that your real life web application will have numerous controllers. However, in this case only two basic controllers are needed to display a Tridion page with its component presentations: PageController and ComponentController. Both of these controllers just inherit ModelControllerBase, a base controller class existing in the framework. Depending on the requirements of your web application, you might want to override methods from this base or implement a new one. For now, it is enough to just leave it as is.
ModelControllerBase contains implementations for both page and component actions: PageModel and ComponentModel.
Routing is set up in a such way that all requests in this example will go to PageController and PageModel action and the URL of the page will be passed to it.
PageModel action is responsible for getting the Tridion page and mapping it to ViewModels. This is done via IViewModelFactory. Note that not only the page is mapped, but also its component presentations. It then renders the view for the mapped model. The view name is this case is determined by the page template name, but it can be overridden in the page template metadata.
ComponentModel action is as simple as it can be. It only renders the given view for the given model. This data can be passed from the page view via a child action.
Rendering Component Presentations
When mapping component presentations to ViewModels, RenderData property is also filled. It contains the information about the component template and its metadata. This determines the action, controller and view name. In case none of this is specified, the component template is used as the view name and the action and controller names are taken from configuration.
There is an HtmlHelper available that can render component presentations from a page view called Render that accepts IRenderableViewModel parameter. This helper is the one calling the child action.
Component view in this case just writes out the heading field.
In case there is a test page in Tridion with Article and CTA components on it and with keywords added in the page metadata, the output HTML would look something like this:
Well… That is it! Happy extending your DD4T 2.0 application!