DNN module development with AngularJS (Part 3)

The view control

In the last blog we finished the service layer. It's time now to start our UI! Let us add a new WebuserControl View.ascx to the root folder of our project:

View control

Code Behind

After adding the control, we have to change some things in the codebehind file:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Angularmodule
    public partial class View : System.Web.UI.UserControl
        protected void Page_Load(object sender, EventArgs e)


The first thing to do is changing the base class of our WebUserControl to a DNN module base class (needs using DotNetNuke.Entities.Modules also):

public partial class View : PortalModuleBase

In the Page_Load event, we insert some code to include the DNN antiforgery and ajax support (needs using DotNetNuke.Services.Exceptions also):

protected void Page_Load(object sender, EventArgs e)
    catch (Exception exc) //Module failed to load
        Exceptions.ProcessModuleLoadException(this, exc);

Normally this is all you need in the codebehind file. But to keep some things simple later we add some more properties to this file helping us with localization, settings and others:


We need a list of all users with username and userid to interactively assign an item to an user. (This could also be done with an WebApi call. But it is easier to include this lookup list data at page generation time so we do not have to do an extra ajax roundtrip for this). The user list is generated as json array string

protected string Users
        var users = UserController.GetUsers(PortalId).Cast<UserInfo>()
            .Select(u => new { text = u.Username, id = u.UserID });
        return ClientAPI.GetSafeJSString(JsonConvert.SerializeObject(users));

additional usings to DotNetNuke.Entities.Users, DotNetNuke.UI.Utilities and Newtonsoft.Json are needed


To be able to use static content localization, we use somethings similar. To hold localized strings for our application we make use of the .NET resource files. Add a special folder App_LocalResources to your project and inside this folder add a new RESX file with the same name as your UserControl: View.ascx.resx. Additional languages are simply added by View.ascx.de-DE.resx etc. In the resx file(s) you can enter a key and the corresponding localized string for every language you want to support. Please make use of the standard DNN syntax for the keys (e.g. cmdSave.Text for the text property of a commandbutton or EmptyField.Error for an error Message). Unfortunately we need an extra using to System.Resources from the System.Windows.Forms reference here. Hope that in later versions of DNN this is not needed because something similar is implemented in the core...

protected string Resources
        using (var rsxr = new ResXResourceReader(MapPath(LocalResourceFile + ".ascx.resx")))
            var res = rsxr.OfType<DictionaryEntry>()
                    entry => entry.Key.ToString().Replace(".", "_"),
                    entry => LocalizeString(entry.Key.ToString()));

            return ClientAPI.GetSafeJSString(JsonConvert.SerializeObject(res));


If we need informations of the module settings it is very handy to have them also onboard:

protected string ModuleSettings
        return ClientAPI.GetSafeJSString(JsonConvert.SerializeObject(Settings));

If someone is logged in who has edit rights to the module and when the module is in EditMode we want to show some additional UI elements:

protected bool Editable
    get { return IsEditable && EditMode; }

needs additional usings to DotNetNuke.Security and DotNetNuke.Security.Permissions

The View

Including Javascript and CSS files

Time to add our references to the angular libs and other needed javascript and css stuff. As mentioned in Part 1 of this blog series, we do not include the needed libs directly in our module. Instead we only reference the installed libraries in DNN:

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="View.ascx.cs" Inherits="Angularmodule.View" %>
<%@ Register TagPrefix="dnn" TagName="JavaScriptLibraryInclude" Src="~/admin/Skins/JavaScriptLibraryInclude.ascx" %>
<%@ Register TagPrefix="dnn" Namespace="DotNetNuke.Web.Client.ClientResourceManagement" Assembly="DotNetNuke.Web.Client" %>
<dnn:JavaScriptLibraryInclude runat="server" Name="AngularJS" />
<dnn:JavaScriptLibraryInclude runat="server" Name="angular-route" />
<dnn:JavaScriptLibraryInclude runat="server" Name="angular-ng-progress" />
<dnn:DnnCssInclude runat="server" FilePath="~/Resources/libraries/angular-ng-progress/01_00_07/ngProgress.min.css" />
<dnn:JavaScriptLibraryInclude runat="server" Name="angular-ui-sortable" />
<dnn:JavaScriptLibraryInclude runat="server" Name="angular-ng-dialog" />
<dnn:DnnCssInclude runat="server" FilePath="~/Resources/libraries/angular-ng-dialog/00_05_01/ngDialog.min.css" />
<dnn:DnnCssInclude runat="server" FilePath="~/Resources/libraries/angular-ng-dialog/00_05_01/ngDialog-theme-default.min.css" />

Unfortunately there is nothing similar for CSS like for Javascript files, so we need to reference the css files with their installed path instead of only using the library name even if the css files are included in the library package.

If you do not want to build the appropriate Javascript packages by yourself (see Part 1 of this blog series) you can download the needed Angular Libraries InstallPackage from here!


The HTML part is very short. The div with the attribute ng-app is the place where all the magic happens later. Important is the fact that the outer div has a unique id (we use the moduleID) so that we can bootstrap Angular later with this ID. In a normal Angular App outside of DNN this is not needed. But in an environment where two or more instances of the same module could reside on the same page this is very important to keep the data apart!

<div id="itemApp<%=ModuleId%>" class="itemApp">
    <div ng-view>Loading...</div>
The Bootstrapping script

When the loading of the page is ready, the Angular app will be initialized. Because we need to send an Antiforgery token together with TabId and ModuleId in the header of each WebApi request (remember the [ValidateAntiForgeryToken] of your WebApi methods?) we preconfigure the Angular $httpProvider with these DNN specific headers.

After initialization we add the properties from our codebehind file (View.ascx.cs) to the app and bootstrap angular on the outer div of our html part.

    angular.element(document).ready(function () {

        function init(appName, moduleId, apiPath) {
            var sf = $.ServicesFramework(moduleId);
            var localAppName = appName + moduleId;
            var application = angular.module(localAppName, [appName])
                .constant("serviceRoot", sf.getServiceRoot(apiPath))
                .config(function($httpProvider) {
                    var httpHeaders = { "ModuleId": sf.getModuleId(), "TabId": sf.getTabId(), "RequestVerificationToken": sf.getAntiForgeryValue() };
                    angular.extend($httpProvider.defaults.headers.common, httpHeaders);
            return application;

        var app = init("itemApp", <%=ModuleId%>, "Angularmodule");
        app.constant("userlist", "<%=Users%>");
        app.constant("resources", "<%=Resources%>");
        app.constant("editable", "<%=Editable%>");
        app.constant("moduleId", "<%=ModuleId%>");
        app.constant("settings", "<%=ModuleSettings%>");
        var moduleContainer = document.getElementById("itemApp<%=ModuleId%>");
        angular.bootstrap(moduleContainer, [app.name]);

Lets run !

There are only few steps before we can run our angular app.

The first thing: Build your application now!

At the moment our module is located in the DNN installation files but DNN does not know about it. So we need to register our module first. Login as a superuser and navigate to Host-Extensions. CLick the last button on top Create new Module. Fill out the form you see in the image and click OK:

Registering the module in DNN

Now the module is registered in the system and you can add it to a page. (I think you know how this works. If not: see some basic videos on youtube about DNN!)

Normally the module should look like this in the page:


as we see, nothing happens! When we take a look in the browser developer tools we see an error:

Error in developer console

Clicking on the error opens an error page in the browser:

Errorpage from Angular

This one does not help because its somewhere in Angular. We first have to click us through by clicking the blue link 3 or 4 times to get to the real error:

The real error

OK: module 'itemApp' is not available. Yeah thats right. We haven't created a Javascript Angular App yet. But that is something for the next part of my blog series!

Total: 0 Comment(s)


Torsten WeggenMy name is Torsten Weggen and I am CEO of indisoftware GmbH in Hanover, Germany. I'm into DNN since 2008. Before this, I did a lot of desktop stuff mainly coded with Visual Foxpro (see http://www.auktionsbuddy.de). 

I'm programmer, husband, father + born in 1965.

Please feel free to contact me if you have questions.

Latest Posts

DNN module development with AngularJS (Part 6)
12/16/2016 7:00 AM | Torsten Weggen
DNN module development with AngularJS (Part 5)
12/16/2016 6:00 AM | Torsten Weggen
DNN module development with AngularJS (Part 4)
12/16/2016 5:00 AM | Torsten Weggen
DNN module development with AngularJS (Part 3)
12/16/2016 4:00 AM | Torsten Weggen
DNN module development with AngularJS (Part 2)
12/16/2016 3:00 AM | Torsten Weggen
DNN module development with AngularJS (Part 1)
12/15/2016 7:19 AM | Torsten Weggen
Blogging in DNN with Markdown Monster by Rick Strahl
11/27/2016 1:14 PM | Torsten Weggen
Creating a global token engine
11/18/2016 10:25 AM | Torsten Weggen
DnnImagehandler - Hot or not ?
2/21/2015 11:52 PM | Torsten Weggen
Rapid Module Development Part 2 - The multilanguage thing…
4/7/2014 7:32 PM | Torsten Weggen

My Twitter

Torsten Weggen 7/22/2017

RT @CBPSC: Ventrian modules now free on Github! https://t.co/aKLeZawVHN #DNNCMS https://t.co/x6mhie382I

Torsten Weggen 5/24/2017

RT @WaldkauzFolk: ++ Waldkauz goes W:O:A 2017 ++ Der Wahnsinn - Wir sind in diesem Jahr beim großartigen Wacken Open Air mit... https://t.…

Torsten Weggen 4/25/2017

RT @WaldkauzFolk: "A magical pagan folk release getting a lot of inspiration of myth, legends and tales from countries all over... https://…