In my previous blog I reviewed the development environment I will be using for my blog series on Module Development in DNN 8. As a reminder you can find all the sample code on Github at https://github.com/cnurse/DnnConnect.Demo. In this blog we will create our first MVC Action and View.
The Model
As mentioned in my previous blog I will be describing how to create a module to manage contacts on my site. The Contact List module will be scoped to the portal – i.e. all contacts for a portal will be available in any instance of the module. A Contact will have 5 simple properties:
- First Name
- Last Name
- Email Address
- Phone Number
- Twitter Handle
As the domain is not what we are really focusing on in this series I have kept it simple. As we will be creating both an MVC module (as well as a SPA module in future blog posts) I have created a separate project for the Model (API) located in the Dnn.ContactList.Api project (Figure 1).
Figure 1 – The Model project
|
|
The Api uses the DAL 2 data access layer and implements a simple “Repository” facade over that framework. As with many of DNN's APIs the ContactRepository is implemented using the ServiceLocator pattern so the methods can be Unit Tested.
The IContactRepository interface provides a simple CRUD API, as shown in Figure 2.
Figure 2 – The IContactRepository interface
|
1: public interface IContactRepository
2: {
3: int AddContact(Contact contact);
4:
5: void DeleteContact(Contact contact);
6:
7: Contact GetContact(int contactId, int portalId);
8:
9: IQueryable<Contact> GetContacts(int portalId);
10:
11: IPagedList<Contact> GetContacts(string searchTerm, int portalId, int pageIndex, int pageSize);
12:
13: void UpdateContact(Contact contact);
14: }
|
The MVC Controller
So now we have everything set up we are ready to create our first MVC Action and View. Following the MVC convention we will create a class in the Controllers folder called ContactController. This class must inherit from the DnnController base class found in the DotNetNuke.Web.Mvc project. This base class inherits from the core MVC Controller base class, but adds extra support for the DNN Module and Portal context. You can think of this class as the equivalent to PortalModuleBase in WebForms module development.
Figure 3 – The ContactController class
|
1: public class ContactController : DnnController
2: {
3: private readonly IContactRepository _repository;
4:
5: public ContactController() : this(ContactRepository.Instance) { }
6:
7: public ContactController(IContactRepository repository)
8: {
9: Requires.NotNull(repository);
10:
11: _repository = repository;
12: }
13: }
|
This class has two constructors. The parameter-less constructor is used by the MVC framework to instantiate the controller, and the second constructor will be used by our Unit Tests. Use of either constructor will set the private IContactRepository variable.
In the future (DNN NeXt) we will be able to dispense with the parameter-less constructor and allow ASP.NET 5’s Dependency Resolver to inject the appropriate implementation of the interface. I have used this approach rather than calling DotNetNuke.Instance.<<Method>> in each action method as this will be the pattern used in DNN NeXt.
The Index Action
Next we need to add an action method – the default method is usually called Index by convention. As we are not really using ASP.NET routing the name is not that important, as DNN actually determines the controller and action to call from the “ControlSrc” property of the Module Control (Contact/Index.mvc).
The Index action is listed in Figure 4.
Figure 4 – The Index Action
|
1: [HttpGet]
2: public ActionResult Index()
3: {
4: var contacts = _repository.GetContacts(PortalSettings.PortalId);
5:
6: return View(contacts.ToList());
7: }
|
This is a fairly simple MVC Action method. In the first line we use the HttpGet attribute to indicate that this method should only be called by HTTP “GET” calls. In line 2 we return an ActionResult – the DNN MVC framework captures this result and renders it in the MvcHostControl. In line 4 we call the GetContacts method of the repository interface to get all the contacts for the portal. Note, that just like PortalModuleBase the DnnController base class provides access to the PortalSettings object. Finally in line 6 we return a strongly typed View, passing in our List of contacts.
The Index View
We now turn our attention to the View. DNN supports all the MVC 5.1 conventions for Views. Thus, you can provide a _ViewStart.cshtml razor script which will execute for all Views before any other View code, and it supports the use of layout views. Figure 5 shows this layout.
Figure 5 – MVC View Conventions
|
|
Lets look at each of these Razor files.
Figure 6 – _ViewStart.cshtml
|
1: @{
2: Layout = "~/DesktopModules/MVC/Dnn/ContactList/Views/Shared/_Layout.cshtml";
3: }
|
The _ViewStart.cshtml file is used to set the Layout property for all the Views. This allows the module developer to use some common “chrome” for all the Views in the module.
Figure 7 – _Layout.cshtml
|
1: <div class="masterLayout">
2: @RenderBody()
3: </div>
|
For this module the shared layout is very simple (being just a wrapper to the required RenderBody() method), but this could be used to register extra css or JavaScript files.
The main view code (Index.cshtml) is shown in Figure 8.
Figure 8 – Index.cshtml
|
1: @inherits DotNetNuke.Web.Mvc.Framework.DnnWebViewPage<IList<Dnn.ContactList.Api.Contact>>
2:
3: <div>
4: @foreach (var contact in Model)
5: {
6: <div class="contactCard">
7: <div>
8: <span>@contact.FirstName</span>
9: <span>@contact.LastName</span>
10: </div>
11: <div>
12: <span>Email:</span>
13: <span>@contact.Email</span>
14: </div>
15: <div>
16: <span>Phone:</span>
17: <span>@contact.Phone</span>
18: </div>
19: <div>
20: <span>Twitter:</span>
21: <span>@contact.Twitter</span>
22: </div>
23: </div>
24: }
25: </div>
|
The first thing to note is Line 1. DNN Views need to inherit from the DnnWebViewPage class. This is because we need to modify some of the helper methods, as well as provide support for DNN context (ModuleContext and PortalSettings). The rest of the code is fairly straightforward Razor script. We loop over all the contacts in the Model property (which is a List of Contact objects), and render the properties of the contact. With some simple styling we can see the View output in Figure 9.
Figure 9 – The View output
|
|
So thats our first MVC Action/View. In my next blog post (on MVC modules) I will show how we can add support for other Actions/Views.
For more information