Products

Solutions

Resources

Partners

Community

About

New Community Website

Ordinarily, you'd be at the right spot, but we've recently launched a brand new community website... For the community, by the community.

Yay... Take Me to the Community!

The Community Blog is a personal opinion of community members and by no means the official standpoint of DNN Corp or DNN Platform. This is a place to express personal thoughts about DNNPlatform, the community and its ecosystem. Do you have useful information that you would like to share with the DNN Community in a featured article or blog? If so, please contact .

The use of the Community Blog is covered by our Community Blog Guidelines - please read before commenting or posting.


Build SPA DNN modules using Razor and WebAPI

How do you build a Dnn Module?

I occasionally read forum threads and blog comments which discuss the myriad of different ways of building a Dnn module.  Usually this is directed towards building modules for storing and displaying content.  Sometimes those are negative – the author is overwhelmed by choice and doesn’t know where to start.

Is it really a problem, though?  Is the problem one of choice, or one of clearly defined paths? 

Flipping this upside down, I see that as a strength of the Dnn Platform as it can cater for a wide audience.  As Dnn powers everything from hobby club sites to multinational corporate websites and everything inbetween, a one-size-fits-all approach would be a negative.   It’s a strength you can choose your own method to build what you want.

Modules built for resale need to be well architected and stick to standards for maximum compatibility and interoperability.  Modules built for a specific corporate solution need to be well made and designed for maintenance and testability, but can at least build to a targeted version.  Modules built for smaller projects need to be biased towards speed to market and easy of update, especially when unpaid work hours are at a premium.

With that background to module styles complete, only a madman would introduce yet another way to Dnn modules.  Here goes, anyway - what I’m going to demonstrate is a way of building modules which will attempt to lasso all of these trends and get them to pull our coding stagecoach.

Specifically, the module must allow:

1.    Single Page Applications (SPA) – performing all the interaction on a single page without subsequent reloads.
2.    Building and running sites in the cloud, and using tools like Visual Studio Online to make changes, rather than having a local development environment and heavy toolset.
3.    Cross-platform development where the person making the changes may not even be able to run Visual Studio.
4.    Moving away from code-behind models like Webforms
5.    Ensuring SEO is front-and-centre during the development phase

Quite a diverse list to pull together and use to create yet another module development template!  Let’s get started by looking at how we can work with these trends by declaring the tools and architecture.

Solving the SEO and SPA divergent goals.

SEO requires control over the URL, Page meta description and Title, and fast-loading static content that doesn’t require script to execute.   Modern search engines do execute script – but optimization is making sure the content meets best practice.
SPA means using AJAX techniques to interact with the page instead of reloading entire pages with postbacks and links to follow.   This can be in conflict with SEO because a SPA might require script to load the initial content for the page.
Solving these divergent goals requires building code that provides an initial view of the content, and then uses API calls via AJAX to load subsequent content on interaction.   The code architecture must allow for a unified way of treating the content two types of view in the same way.  The DNN Page controls provide the ability to set the URL, Title and Description, so this module only concerns itself with presenting static content when the page is loaded.

Building applications on varied devices, including Cloud based development
One of the neat things you get in Azure App Services (the renamed Azure Websites product) is Visual Studio Online – which was code-named Monaco.  This is a save-and-run text editor and basic development environment hosted in the browser.  Now, this isn’t even 1/100th of what a full local development IDE can do, but it is fast, convenient and cross platform.   Cross platform also means development with other tools like Sublime Text or any other code/text editor that takes your fancy.   Moving away from Visual Studio means moving away from MSbuild and compiled solutions and toward edit-and-run changes.  Which brings us to the neatly to the next topic, Webforms and compiled development.

Moving away from Webforms

The movement in ASP.NET development is definitely away from Webforms – this is not recent news.  While DNN is heavily based on the Webforms model, it’s really only the administrative side of things that is dependent on that technology.  Indeed, most of the recent cool functionality in Evoq Content and Evoq Engage is not Webforms based at all.  Indeed, the next major DNN release has MVC functionality built in.

Webforms modules are highly functional and performant if built well, but the code-behind-and-compile model doesn’t fit well with the other goals laid out.  So we need to build with something else, so the Razor view functionality is the perfect choice.

Gathering our design together, what we end up with is the following architecture:
•    A Dnn module built using Razor views and WebAPI, integrated in such a way that there are not two different code libraries for serving each display type.
•    Built in an edit-and-run way, which disposes with the pre-requisite of having Visual Studio (full version) and allows for changes with Visual Studio Online or other text-based code editors.

Here’s how I built that, and how you can re-use the code as a template for your own projects.

NOTE : If there are any code architects reading who just drew sharply through their teeth, it’s true, there are a mixture of patterns here.  I’m providing the barest outline of a module, how well you follow your ViewModel for MVVM and Razor is entirely up to you.  Again it depends on where in the continuum you expect your code to land.  I suspect anyone building a distributable module will use a compiled project, and anyone doing one-off modules in an environment with strict development controls may not prefer these methods.  For everyone else it might provide a simple way forward.  Don’t let architecture perfection be the enemy of finished and functional code.

A new Dnn Module Pattern

All the above points lead into a new Dnn module pattern I have made.   It’s a Dnn module, but uses different architecture to what you might be used to seeing.

Razor Views

A Razor view is a plan Html file with embedded markup allowing interaction with objects and variables from the underlying code.  Razor views were introduced in 2010, so are hardly new technology.  The advantage of a Razor view is the simplicity in the markup, which places more emphasis on getting things done.  A Razor view should be thought of as a simple templating engine.  The resulting Html is much closer in appearance to the template Html, avoiding Html bloat which causes slower pages and broken designs when translated to runtime.

Building Razor with Dnn has been supported for a long time (since 2010, actually) but takeup in my experience has been limited.    Razor brings with it compile-free operation – you just change the view and refresh the page and the change is immediate.

In part that may be because of the paucity of examples of how actually to build a Dnn module with Razor – something that this post seeks to address.

WebAPI and SPA

WebAPI was introduced with DNN 7 and allows an easy way of building API endpoints which can interact with your webpages.   Using WebAPI and a client-side binding framework like Knockout or Angular allows fast creation of Single Page Applications.   There are a lot of existing blog posts and other reference material to WebAPI so I won’t cover it in more detail.  See the WebAPI Tips Wiki page for a good starting point.

Using App_Code Folders

The App_Code folder in ASP.NET is a special folder which allows native C# or VB code to be placed directly into the site without compiling.  Dropping the code in there forces the application to be rebuilt and the compiled classes are then accessible within the application.  This can be used to build any piece of functionality including the Controllers for WebAPI methods and the back end to support Razor views.  You should be alert by now to where this is going.

Hello World Example Module

To demonstrate these techniques in a functional module, I have created a Hello World Razor/WebAPI module.   You can download and install this in any DNN 7.3 or later installation.

This mixes delivery of a common model (the HelloWorldModel) to the page in two distinct ways – via the Razor View and via client-side script calling the WebAPI. 

While you can’t see it in the above image, the first line is delivered by Razor and is ‘static’ in that it is there when the page is built.

The second line is delivered via the WebAPI and added by client side code.

Conceptually it can be shown like this:

There is a base class called RzrViewModel – the Model or ViewModel (your preference) should inherit from this.  This provides the ability for all Models/ViewModels to share common properties.  In this case, it’s sharing the property for the location of the Module WebAPI endpoint, but could be expanded for other Module specific properties.

The HelloWorldModel also implements an interface called IWebAPIResponse which defines a common set of values useful for working in client side code. 

By inheriting from RzrViewModel and implementing IWebAPIResponse, the model is ready to work through both methods of taking data from the server to the device browser.

Connecting the browser to the WebAPI code is done through a Controller class, associated with a RouteMapper class, as is common in all WebAPI code.   We then just use some js code to call into the WebAPI, inspect the result and manipulate the DOM as needed.  Note that this example doesn’t use any MVVM binding like Knockout or Angular – that’s a topic for a different post.

Connecting the browser to the Razor View is done via a class called RzrViewModelLoader, which contains a specific method for returning a HelloWorldModel.   This is done using an extension method to the DnnHelper object.   This uses the Dnn Razor code to associate with a specific view.

Unlike the WebAPI code, this is by configuration rather than by convention.  The WebAPI code knows which method to call because of the naming standards of the Controller and the method, compared with the API call.  The Razor view doesn’t have that (as it might in an MVC project) so we have to explicitly bind the two together.

Inside the Razor view (_view.cshtml), we declare the HelloWorldModel as the root property for the view, and then we can use Razor syntax to access the properties.  This binds the objects with the template, and allows Razor code blocks to be used.

In the Razor View we also can access the property of the RzrViewModel base class to return the correct API.  This might sound trivial, but making portable, flexible code relies on minor setup like this.  Many an API is broken when it is copied from one site to another because the endpoint was hardcoded.

Hosting the Razor view is done with a single ‘view.ascx’.   The view.ascx file is still the file used in the Dnn module manifest to load when the module is added to the page.  This is a container to get the Razor view into Dnn.  All modules have to have a user control of some type.   The other use for the User Control is to leverage the Client Resource Management tools to include the links for javascript we need on the page.  There is no code-behind for this user control (a file exists, but it is empty).

You could include more code-behind user controls to leverage the standard Dnn controls like module settings, or you can build the loading of a settings page into your SPA.   There is no need to have a compiled module even with code-behind ascx files.

Online or Cross-Platform Code Editing

All the above code can be written on any text editor – no Visual Studio or MSBuild needed.  Visual Studio makes the experience nicer, as you get Intellisense and have the ability to run a build on the website to see if you have compile errors – but it’s not a pre-requisite.

Here’s the same file loaded in Visual Studio Online, in an Azure App (Azure Website)

This is where the real benefit comes in – you can edit the files in a copy of your live site, then, when it’s all ready, swap it for the live site in an instant.  This type of instant coding was unheard of the first time I started writing a Dnn module.

Get the code

You can download the code from here:

HelloWorld_Module_01.00.00_Install.zip

Remember, because the source code is compiled at run time, the Install package is also the source code.

The code uses the naming standard of ‘Corp_Name’ ‘Corp_Module’.  This makes it easy to do a global find/replace on ‘Corp_Name’ with your name (or Organization name) and ‘Corp_Module’ with your choice of module name. 

Moving beyond Hello World

This simplistic module was designed only to sketch out the architecture for a Razor/WebAPI module using some commonality.   There are a range of topics not covered, including settings, database access, search integration, permissions and many other Dnn specific topics. 

I will shortly post a second entry in this series which will take this concept further and build a database CRUD app using the same base.  This will provide a more realistic look at building a functional Dnn SPA using these techniques.

In the meantime, please give the module a try, inspect the code and tell me what you think.  I present the source to you, the Dnn Community, in the hope that you might critique it and make it better.

Comments

I’m interested in what people think of the concept.   I’m happy to put the code on Github if people wanted to contribute their own ideas, and stay tuned for Part 2.

Update: Part 2 is now here - see Build a SPA module with Razor, WebAPI, Knockout and jQuery

Comments

kurt wilby
Thanks Bruce,

This is really want I am looking for. After Part 2, maybe its an idea to do an 'Advanced' part for dnn module builders.
kurt wilby Friday, April 24, 2015 4:39 PM (link)
kurt wilby
Great, please put the code on Github.
kurt wilby Friday, April 24, 2015 4:41 PM (link)
Rodney Joyce
Great example Bruce!

I personally am moving to Angular for all my DNN modules - each one is an SPA application - however I am completely moving away from MVC/Razor and using pure client-side code hitting the WebAPI. It does take a bit longer but it's more future proofed.

It would be nice to have a DNN REST API for module settings etc that clientside apps could use...
Rodney Joyce Saturday, April 25, 2015 6:05 PM (link)
Bruce Chapman
@rodney - sure, you can build SPA with just some client side code - but if you're interested in SEO for your target pages then keep the initial load in mind. Plus some things are just easier to push out as a page without writing reams of javascript - Razor syntax is much easier than MVVM binding code, and it is more reliable when it comes to tangled client-side dependencies.

As for the Rest API for module settings - I agree in a way but then it's simple to just build a Method which returns a view of what your module exactly needs, and I prefer that.
Bruce Chapman Monday, April 27, 2015 12:36 AM (link)
Shaun Walker
I have always been a huge proponent of dynamic compilation modules in DNN - especially in use cases where the module is custom built for use within a specific organization. It would not take much effort to create a "SPA" module template from your example and make it available via the Module Creator within the DNN application itself. The added benefit is that it would simplify module creation even more by automating the module registration requirements within the system. Basically you could login to your site and say Create New Module, provide some minimal info such as the module name, and immediately have a functional module you can interact with, including access to the source code via Visual Studio Online.
Shaun Walker Wednesday, April 29, 2015 3:35 PM (link)
Bruce Chapman
@Shaun That's essentially the direction I'm working on this, but I think there would need to be some feedback as to the way this is put together and some actual implementations to make sure the template was worthy of inclusion into the platform. I'm currently building a db-access version which does some simple CRUD work and the more I work on it the more I realise we should probably have a Knockout version and an Angular version for people to choose from as a base.
Bruce Chapman Wednesday, April 29, 2015 5:20 PM (link)
Jay Mathis
Love this post. I've moved primarily to building modules using Angular and I'm looking forward to seeing Part 2 and how this might simplify some of the steps. I tend to avoid the routing parts of because of SEO and just create a separate module control for each 'page' of the app, but the module control just calls the Client Resource Management to load the CSS and JS that I need.
Jay Mathis Thursday, April 30, 2015 3:49 AM (link)
kurt wilby
AngularJS is now adopted by virtually everyone, even Microsoft. The issue in dnn is using Angular with routing. I know that SPA modules will be supported in the next CTPs, but maybe you could integrate this in Part 2 OR give your ideas on JS routing and security (when using .html) and MVC routing.
kurt wilby Thursday, April 30, 2015 6:22 AM (link)
David Poindexter
Excellent Bruce - thank you!
David Poindexter Saturday, June 13, 2015 11:34 AM (link)
Erik Hinds
FYI -

I installed this as a demo on a new 8.0.4 website. It will break your site if you do not have this in your web.config:





Erik Hinds Thursday, October 20, 2016 11:37 AM (link)
Erik Hinds
sorry, code stripped. Review this post...
http://stackoverflow.com/questions/19914455/httpresponsemessage-unavailable-in-app-code-folder-of-vs-2012-project
Erik Hinds Thursday, October 20, 2016 11:37 AM (link)

Comment Form

Only registered users may post comments.

NewsArchives


Aderson Oliveira (22)
Alec Whittington (11)
Alessandra Daniels (3)
Alex Shirley (10)
Andrew Hoefling (3)
Andrew Nurse (30)
Andy Tryba (1)
Anthony Glenwright (5)
Antonio Chagoury (28)
Ash Prasad (37)
Ben Schmidt (1)
Benjamin Hermann (25)
Benoit Sarton (9)
Beth Firebaugh (12)
Bill Walker (36)
Bob Kruger (5)
Bogdan Litescu (1)
Brian Dukes (2)
Brice Snow (1)
Bruce Chapman (20)
Bryan Andrews (1)
cathal connolly (55)
Charles Nurse (163)
Chris Hammond (213)
Chris Paterra (55)
Clint Patterson (108)
Cuong Dang (21)
Daniel Bartholomew (2)
Daniel Mettler (181)
Daniel Valadas (48)
Dave Buckner (2)
David Poindexter (12)
David Rodriguez (3)
Dennis Shiao (1)
Doug Howell (11)
Erik van Ballegoij (30)
Ernst Peter Tamminga (80)
Francisco Perez Andres (17)
Geoff Barlow (12)
George Alatrash (12)
Gifford Watkins (3)
Gilles Le Pigocher (3)
Ian Robinson (7)
Israel Martinez (17)
Jan Blomquist (2)
Jan Jonas (3)
Jaspreet Bhatia (1)
Jenni Merrifield (6)
Joe Brinkman (274)
John Mitchell (1)
Jon Henning (14)
Jonathan Sheely (4)
Jordan Coopersmith (1)
Joseph Craig (2)
Kan Ma (1)
Keivan Beigi (3)
Kelly Ford (4)
Ken Grierson (10)
Kevin Schreiner (6)
Leigh Pointer (31)
Lorraine Young (60)
Malik Khan (1)
Matt Rutledge (2)
Matthias Schlomann (16)
Mauricio Márquez (5)
Michael Doxsey (7)
Michael Tobisch (3)
Michael Washington (202)
Miguel Gatmaytan (3)
Mike Horton (19)
Mitchel Sellers (40)
Nathan Rover (3)
Navin V Nagiah (14)
Néstor Sánchez (31)
Nik Kalyani (14)
Oliver Hine (1)
Patricio F. Salinas (1)
Patrick Ryan (1)
Peter Donker (54)
Philip Beadle (135)
Philipp Becker (4)
Richard Dumas (22)
Robert J Collins (5)
Roger Selwyn (8)
Ruben Lopez (1)
Ryan Martinez (1)
Sacha Trauwaen (1)
Salar Golestanian (4)
Sanjay Mehrotra (9)
Scott McCulloch (1)
Scott Schlesier (11)
Scott Wilkinson (3)
Scott Willhite (97)
Sebastian Leupold (80)
Shaun Walker (237)
Shawn Mehaffie (17)
Stefan Cullmann (12)
Stefan Kamphuis (12)
Steve Fabian (31)
Steven Fisher (1)
Tony Henrich (3)
Torsten Weggen (3)
Tycho de Waard (4)
Vicenç Masanas (27)
Vincent Nguyen (3)
Vitaly Kozadayev (6)
Will Morgenweck (40)
Will Strohl (180)
William Severance (5)
What is Liquid Content?
Find Out
What is Liquid Content?
Find Out
What is Liquid Content?
Find Out