This blog entry explains how you can add some business logic to your site using Model View Controller (MVC). It assumes that you have read through the “Umbraco: Starting with the Home Page” entry.
Within a Razor view (the .cshtml file) we could add a lot of C# style logic constructs to create more sophisticated functionality, but this would soon lead to a lot of code and logic appearing in the views, something that may be undesirable, particularly if we have a separate front-end development team who specialise in HTML and CSS development.
MVC is a code design pattern that separates the business logic that needs to be applied from the view that is displayed to the user.
The model is the data construct that is passed to the view, containing all of the information that it needs to display. This will be a “poco” (plain old C# object), a C# class that simply contains a list of fields that will be used by the view.
We have already met the view, this determines how the model is displayed on the front end. Within Umbraco this will be the Razor .cshtml view, which largely consists of HTML code with some C# style code blocks referencing the model that has been passed in.
The key to MVC is to minimise the amount of logic that is applied within the view. Some logic will always be necessary, but we should focus on keeping the view as straightforward as possible.
This is where any business logic is applied to the information held in the CMS and the model generated for use by the view. The controller will be a C# class that creates the model and all of the information contained within it.
In my previous blog entry I created a simple home page using the Models Builder. We could consider this to be using the MVC pattern. The model was the C# object generated automatically by the Models Builder, the view was the template view that we created (i.e. HomePage.cshtml), and the controller was the Models Builder itself that created the C# object model from the content within the CMS.
That is fine for simple pages, but a lot of the time we need a bit more control over the model passed to the view. Umbraco makes it easy to create a new controller and pass a new model class through to the view.
As an example, I’ll add a top-level navigation bar to my home page. This will be built from a list of child pages under the home page, for example “About us”, “Our products”, “Contact us”. I created a new document type and template for a “Landing Page”, allowed it to be created under my home page and set up my content as follows.
First, I’ll define a new model for the home page that will be passed through to the view. I want to include the fields that we already have, so it makes sense to include the model that was generated by the Models Builder as part of my new model.
I also want a list of landing pages that exist under my home page. Again, I’ll use the models created by the Models Builder, although in this example we could have just used IPublishedContent.
If it doesn’t already exist in the project we need to create an “App_Code” top-level folder. Note, that this is a special ASP.NET type of folder within .Net and should be created by right-clicking on the project and selecting Add→Add ASP.NET Folder→App_Code.
Under App_Code I usually create folders for Models and Controllers. Under the Models folder I added a new HomePageViewModel.cs file, added the required fields and set up a constructor. Note that this class needs to inherit from the Umbraco RenderModel class, in order to inherit some useful Umbraco properties.
public class HomePageViewModel : RenderModel { public HomePageViewModel(HomePage content) :base(content) { Fields = content; Navigation = new List<LandingPage>(); } public HomePage Fields { get; set; } public List<LandingPage> Navigation { get; set; } }
The view can now be updated to display the new model, assuming it has been populated by the controller and passed in. Note the change to the “inherits” line and the use of the UmbracoViewPage, rather than the UmbracoTemplatePage. I have added a new section to display the navigation bar.
@using rtmullinger2018.Models @inherits Umbraco.Web.Mvc.UmbracoViewPage<HomePageViewModel> @{ Layout = "MasterPage.cshtml"; } @* Navigation Bar *@ <div> <ul> @foreach (var page in Model.Navigation) { <li><a href="@page.Url">@page.Name</a></li> } </ul> </div> @* Introduction *@ <p>@Model.Fields.Introduction</p>
When Umbraco tries to render a page in the content tree, it will attempt to render the template view that has been set up on that page. However, before doing that it will check to see if an alternative controller exists matching the following criteria. If it does then control will pass to that controller, rather than directly to the view.
So, to create a controller that takes control when the home page renders, we create a HomePageController.cs class under the Controllers folder, under App_Code. This must inherit from the Umbraco RenderMvcController class and contain a HomePage function to match up to our template name.
public class HomePageController : RenderMvcController { public ActionResult HomePage(HomePage node) { // Create the view model HomePageViewModel vm = new HomePageViewModel(node); // Logic for creating the navigation bar // Use Umbraco libraries to get the child pages, with the correct document type IEnumerable<IPublishedContent> landingPageNodes = node.Children.Where(page => page.DocumentTypeAlias == "landingPage"); foreach (IPublishedContent landingPageNode in landingPageNodes) { LandingPage landingPage = new LandingPage(landingPageNode); vm.Navigation.Add(landingPage); } // Render the view, passing it the new view model return View(vm); } }
You can see that the logic required to look through the Umbraco content tree is implemented in the controller rather than the view, keeping the view as simple as possible.
If you run the website you can now see the controller in action.