Javascript Frameworks

After discovering yet another interesting Javascript framework for the web application development recently (Vue.js), I decided to put together this short list of some the emerging and more popular frameworks out there today for building user interfaces.

Emerging

Mithril is a light-weight robust MVC framework with no dependencies, a small API and small learning curve. Mithril implements a virtual DOM tree similar to React.js, however Mithril’s uncompiled templates run natively in the browser and compile into static Javascript data structures. Mithril.js was initially released in March, 2014 by Leo Horie. Source available at https://github.com/lhorie/mithril.

File Version Size
mithril.min.js 0.2.2-rc.1 19 kb

Vue.js is a library for building interactive web interfaces and its focus is on the view layer only. Vue.js extensions such as vue-router and vue-resource are available for inclusion into the application as needed. The Vue.js API is simple and easy to adapt compared to other frameworks. Vue.js was initially released in July, 2014 by Evan You. Source available at https://github.com/vuejs/vue.

File Version Size
vue.min.js 1.0.13 70 kb

Riot.js – A React-like user interface micro-library. Compare Riot.js with React and Polymer. Source available at https://github.com/riot/riot.

File Version Size
riot.min.js 2.3.12 19.2 kb

Polymer is a framework for creating and rendering custom web components. Web Components are the future of the web and Google has created the Polymer library set of polyfills for developers to dive into. Source available at https://github.com/Polymer/polymer.

Popular

AngularJS a framework by Google that binds HTML (views) to Javascript objects (models). AngularJS usage rivals that of BackboneJS, according to JavaScript analytics service Libscore, AngularJS is used on over 10,000 websites including The Weather Channel, Lego, Intel, Sprint and ABC News. AngularJS was initially released in September 2009 by Misko Hevery. Source available at https://github.com/angular/angular.js.

File Version Size
angular.min.js 1.4.8 145 kb


Ember.js is a framework based on the model-view-controller (MVC) pattern and follows Convention over Configuration (CoC). Ember has a large API that covers traditional MVC patterns and a broad range of helper utilities. Ember 2.0 has a brand new high performance rendering engine named Glimmer, and server-side rendering is provided with FastBoot. After Handlebars in 2010, on December 8, 2011, Yehuda Katz released Ember.js. Source available at https://github.com/emberjs/ember.js.

File Version Size
ember.min.js 2.2.0 435 kb

React.js is the framework that powers Facebook and Instagram’s user interface and is one of the fastest growing JavaScript frameworks today. Features include one-way data flow and a virtual-DOM using an in-memory data structure cache process so only applicable nodes on the client browser’s DOM are updated for high performance complex user interfaces. React.js was created by Jordan Walke, a ‎Software Engineer at Facebook and was initially released in May, 2013 by Facebook and Instagram. Source available at https://github.com/facebook/react.

File Version Size
react.min.js 0.14.6 133 kb
react-dom.min.js 0.14.6 1 kb
Total 134 kb

Backbone.js is a light weight framework that provides a RESTful JSON interface for data models, views, collections, and events. Backbone.js is used on over 12,000 websites according to Libscore including Linkedin, Pinterest, ESPN, Walmart, Target and Best Buy. Backbone.js was initially released on October 13, 2010 by Jeremy Ashkenas. Source available at https://github.com/jashkenas/backbone/.

File Version Size
backbone-min.js 1.2.3 22.5 kb
underscore-min.js 1.8.3 16.0 kb
Total 38.5 kb

AngularJS v2: Angular.io

At ng-conf 2015 last month it was announced that AngularJS 1.X will continue to reside at angularjs.org and Angular 2.0 will be hosted at angular.io. The new version of Angular is not a major update, it is a complete rewrite. In February, Brad Green announced that Angular 2 was officially Alpha in this First look at App Development in Angular2 video.

During the ng-conf 2015 keynote, Brad and Igor talked about some of the big work that is being done on Angular 1 is work that will be shared in Angular 2, including:

  • New Router that focuses on mobile and large complex apps.
  • New Internationalization (i18n) module
  • Better Performance
Angular 2
github.com/angular/angular

AngularJS Cascaded Selects

As a follow up to jQuery Mobile Cascaded Selects using MVC4 and KnockoutJs, I have created this AngularJS Cascaded Selects tutorial that gets it’s data from the Chinook Web API Project. The application uses angular data-binding and the ngOptions attribute to dynamically generate a list of option elements for each of the three HTML select elements.

index.html
<!DOCTYPE HTML>
<html ng-app="ChinookApp">
<head>
	<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.4/angular.min.js"></script>
    <script src="libs/angular-resource.min.js"></script>
	<script src="app.js" type="text/javascript"></script>
	
	<link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.0/css/bootstrap-combined.min.css" rel="stylesheet">
	<link rel="stylesheet" href="app.css">

	<title>Angular Cascading Selects</title>
</head>
<body>
	<div ng-controller="MainCtrl">

        <h2>Angular Cascading Selects</h2>
        <div class="span6 body-content">
            <form class="form-horizontal">
                <div class="control-group">
                    <label class="control-label" for="inputArtist">Artist</label>
                    <div class="controls">
                        <select id="inputArtist"
                            ng-model="artist.selected"
                            ng-options="artist.ArtistId as artist.Name for artist in artists"
                            ng-change="artistChanged(artist.selected)">
                        </select>
                    </div>
                </div>
                <div class="control-group">
                    <label class="control-label" for="inputAlbum">Album</label>
                    <div class="controls">
                        <select id="inputAlbum"
                            ng-disabled="artistNotSelected"
                            ng-model="album.selected"
                            ng-options="album.AlbumId as album.Title for album in albums"
                            ng-change="albumChanged(album.selected)">
                        </select>
                    </div>
                </div>
                <div class="control-group">
                    <label class="control-label" for="inputTrack">Track</label>
                    <div class="controls">
                        <select id="inputTrack"
                            ng-disabled="albumNotSelected"
                            ng-model="track.selected"
                            ng-options="track.TrackId as track.Name for track in tracks">
                        </select>
                    </div>
                </div>
            </form>
		</div>
	</div>
</body>

If your connecting to your REST Web API with a URL that has a port number, such as the built-in Visual Studio Development Server, you will need to escape it with two backslashes:

chinookApp.constant('urlBase', 'http://localhost\\:65374/api/');
app.js
'use strict';

var chinookApp = angular.module('ChinookApp', ['ngResource']);
chinookApp.constant('urlBase', 'http://responsiveresourcesgroup.com/chinook/api/');
chinookApp.config(['$httpProvider', function($httpProvider) {
    $httpProvider.defaults.useXDomain = true;
    delete $httpProvider.defaults.headers.common['X-Requested-With'];
}]);

chinookApp.factory('ArtistsService', function($resource, urlBase){
    return $resource(urlBase + 'artists', {}, {
        query: {method:'GET', isArray:true}
    });
});
chinookApp.factory('AlbumsService', function($resource, urlBase){
    return $resource(urlBase + 'albums', { artistid: '@artistid' }, {
        query: {method:'GET', isArray:true}
    });
});
chinookApp.factory('TracksService', function($resource, urlBase){
    return $resource(urlBase + 'tracks', { albumid: '@albumid' }, {
        query: {method:'GET', isArray:true}
    });
});

chinookApp.controller('MainCtrl', function($scope, ArtistsService, AlbumsService, TracksService) {

    $scope.artistNotSelected = true;

    $scope.artists = ArtistsService.query({}, function (artists) {
        artists.unshift({"ArtistId":0,"Name":"-- Select Artist --"});
        $scope.artist = {selected: artists[0].ArtistId};
    });

    $scope.albumNotSelected = true;
    $scope.album = {selected: "0"};
    $scope.trackNotSelected = true;
    $scope.track = {selected: "0"};

    $scope.artistChanged = function(selectedArtistId) {
        $scope.artistNotSelected = !selectedArtistId;
        if ($scope.artistNotSelected)
        {
            $scope.album = {};
        }
        else {
            $scope.albums = AlbumsService.query({ artistid: $scope.artist.selected }, function (albums) {
                albums.unshift({"AlbumId":0,"Title":"-- Select Album --"});
                $scope.album = {selected: albums[0].AlbumId};
                $scope.albumNotSelected = true;
                $scope.tracks = [];
            });
        }
    };

    $scope.albumChanged = function(selectedAlbumId) {
        $scope.albumNotSelected = !selectedAlbumId;
        if ($scope.albumNotSelected)
        {
            $scope.track = {};
        }
        else {
            $scope.tracks = TracksService.query({ albumid: $scope.album.selected }, function (tracks) {
                tracks.unshift({"TrackId":0,"Name":"-- Select Track --"});
                $scope.track = {selected: tracks[0].TrackId};
            });
        }
    };
});
View, Run, Edit this code in JSFIDDLE

Resources

DNN Session Timer with AngularJS


This tutorial will guide you through the steps necessary to add a session timeout timer and alert dialog within a DotNetNuke (DNN) Module using AngularJS. Key concepts:

  • Using DNN server objects to create AngularJS client-side objects;
  • Using DNN jQuery and jQuery UI registration;
  • Using DNN Client Resource Management;
  • Using DNN Form Patterns

Requirements: DotNetNuke 7; AngularJS; An understanding of how to develop and install DotNetNuke modules. There are a number of resources dedicated to this topic, including my DotNetNuke 7 Module Templates blog post earlier this year.

DotNetNuke Page Setup

The Session Timer is designed to alert the user that they have been inactive for X number of minutes and their session is about to expire. They are presented with a dialog based on a threshold setting defined in a custom module setting to extend the session. Once the session has expired, the dialog content changes to notify the user of their expired session status. Further action will redirect the user to the login page to re-establish a new session. For this to work, your module will need to be placed on a page that is visible only to logged in users. You can check or modify the page permissions while logged in as admin or host at Admin > Page Management.

jQuery UI Registration

Our session alert will be using a jQuery UI modal dialog. Therefore we need to make sure that the jQuery and jQuery UI resources are loaded. jQuery and jQuery-UI are included with DNN. When registering jQuery-UI, it will register the dependent jQuery library as well. In you Module Control code behind, register the jQuery UI resources in the View module control code behind OnLoad or OnInit method:

View.ascx.cs
protected override void OnLoad(EventArgs e)
{
    DotNetNuke.Framework.jQuery.RequestUIRegistration();
}

AngularJS

Download AngularJS and place both angular.min.js and angular.js in a new folder named Library/Angular within your Module folder. For example, I have named my Module “Angular” so here is what the structure looks like in my Visual Studio Solution Explorer:

Suggested location of AngularJS library files within a DNN Module
Suggested location of AngularJS library files within a DNN Module

Create a file for our AngularJS JavaScript. For Visual Studio users, in Solution Explorer, right click on the Angular project, select Add > New Folder, name it “Scripts”. Now right click on the Scripts folder you just added and select Add > JavaScript file and name it SessionTimer.js. For now, add a single line at the top of this file to enable strict mode:

SessionTimer.js
'use strict';

We will use the DNN Client Resource Management API to load our angular.js and SessionTimer.js files. To enable this API in our module, we need to reference two assemblies in the root bin of our DNN website. Therefore, right click on the References folder and browse to the following dll files and reference them:

ClientDependency.Core.dll
DotNetNuke.Web.Client.dll

Add the following markup to the View module control to load the JavaScript resources. As a best practice, we are using the ForceProvider attribute with a value of DnnFormBottomProvider in our DnnJsInclude server controls to load these JavaScript resources at the bottom of the page. Additionally, we are going to use a Priority attribute to ensure they are loaded in the order listed.

View.ascx
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="View.ascx.cs" Inherits="DotNetNuke.Modules.Angular.View" %>
<%@ Register TagPrefix="dnn" Namespace="DotNetNuke.Web.Client.ClientResourceManagement" Assembly="DotNetNuke.Web.Client" %>

<dnn:DnnJsInclude ID="DnnAngularSessionInclude" runat="server" FilePath="~/DesktopModules/Angular/Scripts/SessionTimer.js" ForceProvider="DnnFormBottomProvider" Priority="200" />
<!-- In production, use minified version (angular.min.js) -->
<dnn:DnnJsInclude ID="DnnAngularInclude" runat="server" FilePath="~/DesktopModules/Angular/Library/Angular/angular.js" ForceProvider="DnnFormBottomProvider" Priority="210" />

Now add another line to the SessionTimer.js to define our AngularJS module:

SessionTimer.js
'use strict';

var sessionApp = angular.module('sessionApp', []);

DotNetNuke Module Settings

The Session Timer dialog needs a setting to tell it when to open. One way to store this value is to create a DNN Module Setting for it so it can be easily changed from within the CMS. If you used Christoc’s DotNetNuke Module Development Template to create your module, you should have a Settings module control stubbed out and ready for you to add the Session Timer Threshold setting controls so the value in minutes can be stored as a module setting in the DNN database. The Settings module control markup and code for this is below.

Settings.ascx
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="Settings.ascx.cs" Inherits="DotNetNuke.Modules.Angular.Settings" %>
<%@ Register TagName="label" TagPrefix="dnn" Src="~/controls/labelcontrol.ascx" %>

<h2 id="dnnSitePanel-BasicSettings" class="dnnFormSectionHead"><a href="" class="dnnSectionExpanded"><%=LocalizeString("BasicSettings")%></a></h2>
<fieldset>
    <div class="dnnFormItem">
        <dnn:Label ID="lblSessionTimerThreshold" runat="server" /> 
        <asp:TextBox ID="txtSessionTimerThreshold" runat="server" />
    </div>
</fieldset>
Settings.ascx.cs
using System;
using DotNetNuke.Entities.Modules;
using DotNetNuke.Services.Exceptions;

namespace DotNetNuke.Modules.Angular
{
    public partial class Settings : AngularModuleSettingsBase
    {
        #region Base Method Implementations

        /// -----------------------------------------------------------------------------
        /// 
        /// LoadSettings loads the settings from the Database and displays them
        /// 
        /// -----------------------------------------------------------------------------
        public override void LoadSettings()
        {
            try
            {
                if (Page.IsPostBack == false)
                {
                    if (Settings.Contains("SessionTimerThreshold"))
                        txtSessionTimerThreshold.Text = Settings["SessionTimerThreshold"].ToString();
                }
            }
            catch (Exception exc) //Module failed to load
            {
                Exceptions.ProcessModuleLoadException(this, exc);
            }
        }

        /// -----------------------------------------------------------------------------
        /// 
        /// UpdateSettings saves the modified settings to the Database
        /// 
        /// -----------------------------------------------------------------------------
        public override void UpdateSettings()
        {
            try
            {
                var modules = new ModuleController();

                modules.UpdateModuleSetting(ModuleId, "SessionTimerThreshold", txtSessionTimerThreshold.Text);

            }
            catch (Exception exc) //Module failed to load
            {
                Exceptions.ProcessModuleLoadException(this, exc);
            }
        }

        #endregion
    }
}

Settings.ascx.resx

To make use of the label control (dnn:Label) that is registered at the top of the Settings module control file, edit the respective resource (.resx) file as shown in the image below. The label control provides label text, and tool-tip text for additional information.

Settings.ascx.resx for our SessionTimer Threshold value in the Angular module settings form
Settings.ascx.resx for our SessionTimer Threshold value in the Angular module settings form

Add a SessionTimerJavaScript method to the AngularModuleBase class so all module controls in the Angular module have access to it. This method loads the SessionTimer.js and AngularJs module constants for the dialog display threshold and session duration. Here is what the file looks like with this method added.

AngularModuleBase.cs
using DotNetNuke.Entities.Modules;

namespace DotNetNuke.Modules.Angular
{
    public class AngularModuleBase : PortalModuleBase
    {

        public static void SessionTimerJavaScript(PortalModuleBase portalModuleBase)
        {
            if (portalModuleBase.Page != null)
            {
                string timerScript = "";

                int sessionWarningThresholdInMilliSeconds = int.Parse(portalModuleBase.Settings["SessionTimerThreshold"].ToString()) * 60000;
                int sessionDurationInMilliSeconds = portalModuleBase.Page.Session.Timeout * 60000;

                //guard against invalid warning threshold.
                if (sessionWarningThresholdInMilliSeconds >= sessionDurationInMilliSeconds)
                {
                    sessionWarningThresholdInMilliSeconds = sessionDurationInMilliSeconds - 1000;
                }

                string timerVars = string.Format("sessionApp.constant('settings',{{ threshold: {0}, sessionDuration: {1} }});\n", sessionWarningThresholdInMilliSeconds.ToString(), sessionDurationInMilliSeconds.ToString());

                if (!portalModuleBase.Page.ClientScript.IsStartupScriptRegistered("_SessionTimerInclude_"))
                {
                    portalModuleBase.Page.ClientScript.RegisterStartupScript(portalModuleBase.Page.GetType(), "_SessionTimerInclude_", timerScript, false);
                }
                if (!portalModuleBase.Page.ClientScript.IsStartupScriptRegistered("_SessionTimerVars_"))
                {
                    portalModuleBase.Page.ClientScript.RegisterStartupScript(portalModuleBase.Page.GetType(), "_SessionTimer_", timerVars, true);
                }
            }
        }

    }
}

For now, edit the Angular module settings entering a value of 18 for the session timer threshold. This will make it so the session dialog opens only after 2 minutes with a default session duration of 20 minutes. Typically, you will want to use a value of 2 for the threshold, but we don’t want to have to wait 18 minutes for the dialog to open while we test it out.

Add the SessionTimerJavaScript method call to your View module control OnLoad method as follows.

View.ascx.cs
protected override void OnLoad(EventArgs e)
{
    DotNetNuke.Framework.jQuery.RequestUIRegistration();

    //load AngularJS Session Timer
    SessionTimerJavaScript(this);
}

Build the module and refresh the page in DNN. When you view the page source in the browser, you should see the following near the bottom of the page if your session timer threshold is set to 18 minutes and session duration is the default of 20 minutes:

HTML view source

<script src="/DesktopModules/Angular/Scripts/SessionTimer.js" type="text/javascript">/script>
<script type="text/javascript">
//<![CDATA[
sessionApp.constant('settings',{ threshold: 1080000, sessionDuration: 1200000 });
//]]>
</script>

jQuery UI Dialog

Add this jQuery UI markup to the bottom of the default (View) module control.

View.ascx

<div ng-app="sessionApp" ng-controller="SessionCtrl">
    <div id="dialog-session" title="Session Expiration">
        <p>Your session will expire in:</p>
        <p>{{ remainingTime | minute }} minutes.</p>
        </div>
    </div>
</div>

Add this css to the module stylesheet so the dialog is hidden when the View module control loads.

module.css
#dialog-session {
    display: none;
}

Session Timer AngularJS Timeout

Below the sessionApp angular module variable that was added last time we touched this JavaScript file, add the variable for the jQuery UI dialog selector, sessionAppTimeoutId var, sessionApp filter for dialog minutes display and sessionApp controller code so you file looks like this:

SessionTimer.js
'use strict';

var sessionAlert = $("#dialog-session");

/* module def */
var sessionApp = angular.module('sessionApp', []);
var sessionAppTimeoutId;

/* filters */
sessionApp.filter('minute', function () {
    return function (milliseconds) {
        var seconds = Math.floor(milliseconds / 1000);
        var minutes = Math.floor(seconds / 60);
        seconds = seconds % 60;
        return minutes + ":" + (seconds < 10 ? "0" + seconds : seconds);
    }
});

/* controllers */
sessionApp.controller('SessionCtrl', ['$scope', '$timeout', '$window', 'settings', function ($scope, $timeout, $window, settings) {

    $scope.checkInterval = 1000;
    $scope.threshold = settings.threshold;
    $scope.duration = $scope.remainingTime = settings.sessionDuration;

    $scope.hide = function () {
        sessionAlert.dialog("close");
    }
    $scope.displayDialog = function () {
        
        //remove logging in production
        console.log('displayDialog');
       
        sessionAlert.dialog({
            dialogClass: "dnnFormPopup",
            modal: true,
            resizable: false,
            buttons: [{
                text: "Extend Session",
                click: function () {
                    $window.location.reload();
                }
            }]
        });
    }
    $scope.checkSession = function () {
        $timeout.cancel(sessionAppTimeoutId);

        $scope.remainingTime = $scope.remainingTime - $scope.checkInterval;

        //remove logging in production
        console.log($scope.remainingTime);

        if ($scope.remainingTime === settings.threshold) {
            $scope.displayDialog();
        }

        if ($scope.remainingTime > 0) {
            sessionAppTimeoutId = $timeout($scope.checkSession, $scope.checkInterval);
        } else {
            sessionAlert.dialog("option", "buttons", [{ text: "Ok", click: function () { $window.location.reload(); } }]);
        }
    }
    $scope.start = function () {
        sessionAppTimeoutId = $timeout($scope.checkSession, $scope.checkInterval);
    }

    $scope.start();

}]);

Forms Authentication (added July 10, 2013)

The default DNN installation uses Forms Authentication that will log out the user after 60 minutes of inactivity as shown in this web.cofig node:

web.config
<authentication mode="Forms">
    <forms name=".DOTNETNUKE" protection="All" timeout="60" cookieless="UseCookies" />
</authentication>

To make use of the forms timeout since that overrides the Session duration value we are currently using, in AngularModuleBase.cs:

Replace

int sessionDurationInMilliSeconds = portalModuleBase.Page.Session.Timeout * 60000;

With

int formsTimeoutDurationInMilliSeconds = System.Web.Security.FormsAuthentication.Timeout.Milliseconds;

Refactor the respective sessionDurationInMilliSeconds var names in the remainder of the SessionTimerJavaScript method with the new name: formsTimeoutDurationInMilliSeconds.

This project is available for browsing and download at GitHub:
https://github.com/jimfrenette/DnnAngular

To Do List…

  1. Check permissions and if the page is public, do not the start the Session Timer.
  2. Add validation to the Session Timer Threshold input so its value cannot be set higher then the session duration.

Resources