ASP.NET · C# · Dotnet · Javascript · Jquery

Dilettante attempt @MustacheJS with ASP.NET

Dear Reader,

For some reason i stumbled upon the javascript templating topic and found out about MustacheJS and its simplicity, though i am not an expert at this topic, but i felt the ease of learning this is quite nice.

It took me for a while to understand and proceed in getting started with Mustache. My skills with JS is also needs a lot of sharpening, yet its slow and steady. In attempt to learning this engine, i made a sample application which is easy enough to understand myself in future as well as any others.

With that in mind, i came up with a small application of getting some messages data based on the options/choices chosen by a user i.e Admin vs Guest. Its tiny and simple but i hope its very easy to understand. It might look like re-inventing a wheel, but i wanted to try in my own way.

Default.aspx:

Untitled

As you can see, i have only 2 radio buttons on the default page: Admin and User. The rest is all self-explanatory.

Admin.html:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<div id="AdminTemplate">
    <h2>Welcome aboard {{UserName}}</h2>

    <h3>select type of action:</h3><input id="Manage" name="SettingsType" onclick=
    "OnSettingsTypeRadioButtonClicked(this)" type="radio" value="Manage">Manage <input id="News"
    name="SettingsType" onclick="OnSettingsTypeRadioButtonClicked(this)" type="radio" value=
    "News">News <input id="Tasks" name="SettingsType" onclick=
    "OnSettingsTypeRadioButtonClicked(this)" type="radio" value="Tasks">Tasks List <input id=
    "Messages" name="SettingsType" onclick="OnSettingsTypeRadioButtonClicked(this)" type="radio"
    value="Messages">Messages

    <div id="TemplateHolder"></div>
</div>

As you can see from the above html page, i have added a place holder for a dynamic text from server at line 2; the place holder for mustache to recognize is placed between {{xxxx}} braces. Then there are few radio buttons provided to choose.

User.html:

 1
 2
 3
 4
 5
 6
 7
<div id="UserTemplate">
   <h2>Welcome aboard {{UserName}}</h2>

    <h3>Below listed messages. Have fun</h3>
    <div id="TemplateHolder"></div>

</div>

The template or the data for the user is kept very simple without any option to make it very distinguishable.

The div element with id= “TemplateHolder” is the tag which will hold a message board for both Admin and Guest user based on their choice; if any.

In this example, i wanted to list down the messages in a bulletin format or list wise, hence i chose below message board template:

MessagesBoardTemplate.html:

 1
 2
 3
 4
 5
 6
 7
 8
<div id="MessagesBoardTemplate">
    <h4>{{ChoiceMessage}}</h4>
    <ul>
        {{#ChoiceInfo}}
        <li>{{.}}</li>
        {{/ChoiceInfo}}
    </ul>
</div>

In Mustache, one can print an array items in the form of a list as shown above. The array start is done on-line 4 and the line 5 i.e {{.}} iterates (internally to MustacheJS) over each item of the array and substitutes it.

Now comes the part where we need to fetch the data and then prepare the template info to be displayed.

Template.js:
Snippet 1:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
function OnRadioButtonClick(element) {
    if ($(element).attr("id") == "Admin") {        
        
        getHTML(function (html) {
            processTemplateHTML(html, "Admin");
        }, "/Templates/Admin.html");       
    }
    else if ($(element).attr("id") == "Regular") {
        getHTML(function (html) {
            processTemplateHTML(html, "Guest");
            requestMessageData("Messages");
        }, "/Templates/User.html");
    }
}

As you can see from the above code, this function/event gets executed when the user selects an option on Default.aspx page.

getHTML is a callback variable (delegate in .net terms) at line 4 and 9, used here to invoke/load the template/html data from the server. Since every thing in Javascript is an asynchronous operation, the code flow here is a continuous styled. Making it a synchronous operation kills the usability/operational style of the browser, hence the delegate/callback approach is used here.

Snippet 2:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
var getHTML = function (onResult, url) {
    $.get(url, function (htmlContent) {
        onResult(htmlContent);
    });
}

function processTemplateHTML(templatehtml, userName) {
    var uName = { UserName: userName };
    var htmlContent = Mustache.to_html(templatehtml, uName);
     $("#UserTypeDataHolder").html(htmlContent);
}

From the above code, the getHTML callback variable is declared at line 1 here which holds a reference to a function taking 2 parameters. The first argument is another callback function being passed in the previous snippet 1 at line 4 and 9. Here, i am using Jquery.get() method to get the html content from the server. I am not using .load() method here because, i need the html content in a in memory variable than being appended to an DOM tree element; which may or may not be made hidden. The .load method does not load the content to a variable in memory nor to a dummy div tag created by the jquery, hence the next best is chosen.

OnResult argument is another delegate (remember the continuous code flow layout of javascript due to its asynchronous modus operandi?) being passed which is; processTemplateHTML() at line 7. This method, takes the template (html from server) and then creates an associative array (line 8), this is then passed to the MustacheJS at line 9. The musatcheJS thus parsing the html content will place the values being supplied in the second argument onto the place holders in the first argument at suitable matching places.

In above snippet, at line 10, the template data holder is assigned the final html content for further operation using jquery.

Snippet 3:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function OnSettingsTypeRadioButtonClicked(element) {    
    requestMessageData($(element).attr("id"));
}

function requestMessageData(elementId)
{
    getHTML(function (html) {
        $.ajax({
            type: "POST",
            url: "Default.aspx/GetSettingsActionData",
            data: JSON.stringify({ "choice": elementId }),
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            success: function (data) {
                setTemplateData(data, html);
            },
            error: function (request, status, error) {
                alert(request.responseText + " ===== " + status + "---------" + error);
            }
        });
    }, "/Templates/MessageBoardTemplate.html");
}

function setTemplateData(jsonData, actionTemplateData) {
    var html = Mustache.to_html(actionTemplateData, JSON.parse(jsonData.d));
    $("#TemplateHolder").html(html);
}

On line 1 above, the method/event OnSettingsTypeRadioButtonClicked() is invoked by the multi choice radio button provided by the Admin.html template.

requestMessageData() function invokes a server AJAX request to the ASP.NET code behind (c#) method to fetch the dynamically populated data based on the choices made. Line 10 is a better choice to make things obvious of the data conversion being done, some times the ajax request fails if this is not used i.e explicit JSON string conversion. The line 12, is also a must, if data is passed to the server via “POST” method in AJAX request: this helps the code behind and the browser to decipher the data better.

On success request, setTemplateData() callback is invoked, wherein the json data fetched from the server is applied to the MessageBoardTemplate html. Please note that the json data received must be parsed back to JSON at line 26 else the Mustache fails to parse the string.

C# Code behind:

Default.aspx.cs:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
public class SettingsData
    {
 
        public string ChoiceMessage {get;set;}

         public List<string> ChoiceInfo { get; set; }
    }

    public partial class Default : System.Web.UI.Page
    {
        [System.Web.Services.WebMethod]
        public static string GetSettingsActionData(string choice)
        {
            SettingsData data;
            switch (choice)
            {
                case "Manage":
                    data = GetConfigData();
                    break;
                case "News":
                    data = GetNewsData();
                    break;
                case "Tasks":
                    data = GetTasksData();
                    break;
                case "Messages":
                    data = GetMessagesData();
                    break;
                default: data = null;
                    break;
            }

            string json = new JavaScriptSerializer().Serialize(data);
            return json;
        }

        public static SettingsData GetConfigData()
        {
            SettingsData d = new SettingsData();

            d.ChoiceMessage = "Choices";
            d.ChoiceInfo = new List<string>()
            {
                "User Configuration",
                "WebPages Configuation",
                "DateTime Congfiguration",
                "Performance Tests",
                "Server logs Configuration"
            };

            return d;
        }

        public static SettingsData GetNewsData()
        {
            SettingsData d = new SettingsData();

            d.ChoiceMessage = "Important events:";
            d.ChoiceInfo = new List<string>()
            {
                "10 Users at server x.x.x.10 blocked",
                "2 Users at server x.x.1.20 granted additional rights",
                "Servers at 10.3.3.5 went offline for 4 hours",
                "Users at 2.3.101.222, 9.89.32.22, 2,3.51.55 were logged in for more than 36 hours",
                "System detected users at 10.0.0.1, 0.2.3.4, 21.1.2.3, 33.2.12.4 for suspecious activity"
            };
            return d;
        }

        public static SettingsData GetMessagesData()
        {
            SettingsData d = new SettingsData();

            d.ChoiceMessage = "Messages";
            d.ChoiceInfo = new List<string>()
            {
                "Your password about to expire in 10 days",
                "Your last login was 2 days"                
            };
            return d;
        }

        public static SettingsData GetTasksData()
        {
            SettingsData d = new SettingsData();
            d.ChoiceInfo = new List<string>()
            {
                "Server 2.1.3.200 needs restarts",
                "Server 20.4.65.22 updates pending",
                "Database upgrade done, restart required for server oxc.edm.com"
            };
            return d;
        }
    }

The above code is mostly self-explanatory. Based on the selection choices,i am filling up the DataObject and at line 33, its recommended to use System.Web.Script.Serialization.JavaScriptSerializer API for serializing the managed type objects than the API’s from System.RunTime.Serialization assembly.

I hope i have made things simple and clear. If any mistakes/suggestion, kindly please leave a detailed comment, if possible.

Thanks,
Zen πŸ™‚

Sample: https://drive.google.com/open?id=0B5DC25Y3gdgrR3BzRk1CQzNobEE&authuser=0

References:

Advertisements

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s