DotNetNuclear
  • About
    • Contact
  • Products
    • Intensity Slider
    • MVP Template
    • Support
  • Learning

Advanced DotNetNuke Rich Client Modules - Knockoutjs, jQuery and Services Framework
Article Rating

By  Scott Wilkinson

Posted Apr 28, 2013 09:53

Tweet

Comments 10

Introduction

This article and video recaps the Rich Client Module Development session I presented at the Southern Fried DNN Conference Day of DNN day on April 13, 2013. The session was a short 1 hour presentation of a large topic. The goal of the session was to show a new paradigm for module development that takes us beyond webforms development which focuses on server-side page composition and moves us into developing modules that compose the page mostly from the client. This is an important evolution for DNN developers to not only get less dependent on server side controls, but also translates into skills for mobile development and other next generation technologies.


The Data Visualizer module

First I demonstrate the functionality of the Data Visualizer module that I built for this presentation. This module is a functioning chart generator. You can change the chart type from bar to line or pie, flip the axis, change the dimensions, or edit the caption. After every change, you will see the chart re-render itself dynamically without any page postbacks. You can also change the chart series data (which I preloaded). This data is stored in a table and, if changed from the dropdown, the chart will re-render itself with posting back the page.


DotNetNuke module architecture (typical and best practice)

Next I show how we must evolve our module development architecture in 2 ways:

  • Enhancing our module projects with MVP and other patterns to make them more scaleable and maintainable applications that can be unit tested
  • Making our architecture support rich client development by way of using DNN framework service that can be accessed directly by our javascript

The Data Visualizer technology stack

Here is a rundown of the stack:

  • DNN Services Framework
  • DAL2 Data Layer
  • JSON
  • jQuery Ajax
  • jQuery Visualize plugin (Html5 Canvas)
  • Knockoutjs

Developing DotNetNuke 7 Framework Services and Client

Our chart plugin expects its data to come from an html table. So to get the data and build the html table dynamically, we create a DNN framework service to feed us a json object that consists of the chart's default options and series data that is stored in the database. Then we use knockout templating to build an html table out of the json object. So we start by creating the tables and DAL2 layer. For more detail on the DAL2 development in DNN7, see my DNN7 complete course training.

Next we expose our data with a DNN7 framework service. For a tutorial of the DNN services framework, check out my introduction study article. To keep it simple, I am just using anonymous authorization for this data service. It is probably more appropriate to use DnnModuleAuthorizeAttribute for this. Check out Brian Duke's presentation about Mobile APIs or Peter Donker's article on Authorization in the new DNN Services Framework for some great study on this topic.

Now that we have our service working to give us json data, we build the ajax function that will call it.

function getChartData(chartId, serviceRoot) {
    jQuery.ajax({
        type: "GET",
        url: serviceRoot + "chartdata/datasets/" + chartId,
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success: function (response) {
            ko.applyBindings(new ChartViewModel(response));
        },
        error: function (xhr, ajaxOptions, thrownError) {
            alert("status: " + xhr.status + "\n" + thrownError);
        }
    });
}

KnockoutJS

Once we retrieve our chart data from the service, our ajax function will initialize our knockoutjs view model. Knockout is used to bind our chart options to their respective form elements thereby controlling the event handling between the view model and the DOM elements. It is also used to create the series data html table for consumption by our chart plugin using it's template engine. There is a lot about knockout that I cannot properly teach in this tutorial, so if you are brand new to it, go to knockoutjs.com and go through their tutorials to get a basic understanding.

For the chart options, we create observable attributes in our viewmodel that are initialized to our corresponding chart data fields.

self.title = ko.observable(chartData.Title);

Then we add the data-bind attribute to the DOM element that we want to edit the attribute.

<input type="text" name="title" id="title" class="longSingleText" data-bind="value: title" /> 

Then we add a computed function to compose any options change into the options array that the chart plugin needs by using a computed function.

self.chartOptions = ko.computed(function () { … } , this);

For our series data, we create an observable array which is a copy of the chart data's series array. We probably could skip this step, but it makes a future possibility of binding the data to textboxes to make it editable.

// Bind the series data
var seriesObservableArray = ko.observableArray();
for (i = 0; i < chartData.SeriesData.length; i++) {
   series = chartData.SeriesData[i];
   seriesObservableArray.push(new Series(series.Name, series.Values));
}
self.seriesData = seriesObservableArray;

Now we use knockout's template engine to write the html table using the observable array. A better way to do this is to modify the jquery plugin to take the json data directly, but I didn't want to mess with that code for this demonstration.

<tbody data-bind="template: { name: 'bodyTmpl', foreach: seriesData }, visualize: chartOptions"> … 

Finally we create a custom event in knockout to refresh the jQuery chart plugin when either the chart data or chart options are updated

// Create handler for refreshing the chart based on setting or chart data update
ko.bindingHandlers.visualize = {
    update: function (element, valueAccessor, allBindingsAccessor) {
            // get value which should be chartoptons
            var value = ko.utils.unwrapObservable(valueAccessor());
            $('.visualize').remove();
            if (value != null)
                $('#VDatatable').visualize(value);
    }
}

Installing the project

To install the project and start building onto it, do the following:

  • Download the code project zip file
  • Unzip the project into the DesktopModules folder of you DotNetNuke 7 installation
  • Open the project in Visual Studio and do a release build
  • In the project root folder, there should be an install folder with some zip packages, go to your DNN installation in the browser and install the source package
  • Once installed, the module can be added to a page for testing.
  • Download the SQL data script and run it to create the test data like I have. You will have to change the moduleID in the inserts to match your moduleID.

Related Files

  • SQL: DataVisualizer_Data_SQLScript (15.3 KB)
  • Slides: Rich Client Module Development (1.09 MB)
  • Code: DNNuclear.DynamicVisualizer (5.68 MB)

Related Articles

  • Advanced DotNetNuke Module Development - MVP Pattern
  • DotNetNuke Module Development Basics

Rate this


Post Comment

Name (required)

Email (required)

CAPTCHA image
Enter the code shown above:

10 Comments

MVPman
# MVPman
Monday, April 29, 2013 5:31 PM
When trying to add the module to a page I get this (error)message: 'ddlChartData' has a SelectedValue which is invalid because it does not exist in the list of items. Parameter name: value.
Is there a fix for this???
host
# host
Monday, April 29, 2013 6:25 PM
MVPman,

Thanks for pointing this error out. The errors were from the lack of demo data. I rebuilt the code to work with empty data so it won't throw an error. Re-download the project and replace it. Also, download the SQL script (I fixed the file above). Replace the two occurrences of the ModuleID in the Chart table inserts to the moduleID on your page. Then run the script. You should have a working demo like mine. Let me know if you have any more issues.

Thanks!
MVPman
# MVPman
Friday, May 03, 2013 12:34 PM
The Chart Data DDL is not working! Is this true?
Scott
# Scott
Friday, May 03, 2013 12:49 PM
MVPMan,

Did you replace the project, modify the above SQL data script with your module ID, and rebuilt the project? If so, the project should have no errors and the dropdown should populate. Confirm you have two tables in your DNN instance: DNNuclear_DataVisualizer_Chart and DNNuclear_DataVisualizer_ChartData and that they have data. The moduleID field in the DNNuclear_DataVisualizer_Chart table should be populated with the moduleID in your instance. Email me if you still need help.
MVPman
# MVPman
Wednesday, May 08, 2013 8:21 AM
Thanks, I meant there was no data in the ddl.

Do you have a full CRUD example for this project? How would you add/create Category ... Update them. Delete them (using .js/ko).


Ricardo Rocha
# Ricardo Rocha
Sunday, August 11, 2013 7:49 PM
I am having trouble with your code. I get the error mention below:

ddlChartData' has a SelectedValue which is invalid because it does not exist in the list of items. Parameter name: value

I am able to correct the error by adding a valid value to the ChartId => ChartId = 4 (4 being a valid value);

I tried to identify when and where ChartId can potentially get the correct value without any luck. ChartId is always 0

Can you help me?

Now my code in the view.ascx looks like this:


protected void Page_Load(object sender, EventArgs e)
{
try
{
//Send the ModuleLoad event of the Presenter
var args = new ViewLoadEventArgs { IsPostBack = this.Page.IsPostBack };
if (ModuleLoad != null) { ModuleLoad(this, args); }
if (!IsPostBack)
{
try
{
ddlChartData.DataSource = ChartDatasetList;
ddlChartData.DataTextField = "Value";
ddlChartData.DataValueField = "Key";
ddlChartData.DataBind();
if (ChartDatasetList.Count > 0)
{
ChartId = 4; //This is my addition Ricardo Rocha

ddlChartData.SelectedValue = ChartId.ToString();

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


host
# host
Sunday, August 11, 2013 8:44 PM
Ricardo,

The list of chartId's for the ddlChartData dropdown come from the table: DNNuclear_DataVisualizer_Chart. Make sure you loaded the list from the script I gave but changes the ModuleId each record to match with yours. The line: ddlChartData.SelectedValue = ChartId.ToString(); attempts to select a default chartid to load from the settings. Click on the module settings and select the default chart there. That should give you a valid chartid when loading the view.
Ricardo Rocha
# Ricardo Rocha
Monday, August 12, 2013 1:11 AM
Scott,

Thank you for your time and patients.

I loaded the sql script and I changed the ModuleId to match the ModuleId of my Module in the page.

I dont think the part where we save the settings is working ("Data Vizualizer Settings"). The dropdownlist on the Data Visualizer Settings displays both of my record (choices). However, after t I select one of the choices the module does not retain the value.

I ran some debugging on the module and I am not able to find where in the code the Initial Chart Dataset Value is stored and/or save. Through the whole debugging the ChartId value is 0.

Maybe, I am not entering the in-correct ModuleId on the Database records ( 2 records).

Can you attempt to load the module in your side and see if you get the same error?

Ricardo Rocha

Asle
# Asle
Monday, August 12, 2013 5:25 PM
How do I find the ModuleID for the SQL-script?

I did a select from dbo.DesktopModules but using that ModuleID does not return any data in the diagram. The "ChartData" drop down box is empty.
host
# host
Tuesday, August 13, 2013 10:59 AM
All,

I finally had a moment to look at my code and found the issue that many were having with the chartData.dll. The problem was a bug in how I was accessing the module settings. I was using TabId instead of TabModuleId to access the setting. For example, in the settings.ascx.cs, I use Components.SettingsRepository settingsCtrl = new Components.SettingsRepository(this.TabId); The TabId should be TabModuleId. I updated the project zip file in the article with the correct code. Please download and setup the code in your environment. I followed these steps to finish setting up the module:
1. Did a release build to create the install package in the module project's install folder
2. Used the install package to install and register the module in DNN
3. Added an instance of the module on a page
4. Ran the following Query to get my module's id:
SELECT m.ModuleID
FROM dbo.Modules m INNER JOIN dbo.ModuleDefinitions def
ON m.ModuleDefID = def.ModuleDefID
WHERE def.FriendlyName = 'Dynamic Vizualizer'
5. Downloaded the Data SQL script from the article, replaced the moduleID 477 with my moduleid in the two insert statements at the top. Ran the sql script in my database.
6. Refresh the Cache
7. Go to the module page, click on module settings, select a default data set and click update.
Did this help you? Give back... Give $5 so we can continue creating these articles Why?
25 Latest Articles Current Articles | Archives | Search
  • © 2019 DotNetNuclear LLC
  • Terms Of Use |
  • Privacy Statement |
  • Login
  • DNN Software
  • YouTube
  • Twitter
  • LinkedIn
  • Facebook
  • Stack Overflow