Serializing .NET Resources (.resx) as JSON (part 2)

So after playing with my own code after a while I realized that there was a slight flaw in my thinking.  Since every application is slightly different in the way it determines what culture to use, and that each request is technically a different Thread, I need a more generic way to capture that information.

I decided to use a querystring value.  Here is my updated code in regards to reading the querystring culture, setting it and fired up the appropriate ResourceManager..

 

ResourceManager rm = new ResourceManager(ConfigurationManager.AppSettings["JSResourcesAssemblyType"].ToString(),
                Assembly.LoadFile(ConfigurationManager.AppSettings["JSResourcesAssemblyPath"].ToString()));
            if (context.Request.QueryString["CultureCode"] == null) return;
            var culture = context.Request.QueryString["CultureCode"].ToString();            
            ResourceSet rs = rm.GetResourceSet(new CultureInfo(culture), true, true);            
            var sbInitial = "var rm = {";
            var sb = new StringBuilder(sbInitial);            
            var resEnum = rs.GetEnumerator();
            while (resEnum.MoveNext())
            {
                if (sb.ToString() != sbInitial) sb.Append(",");
                sb.Append("\"" + resEnum.Key + "\":\"" + 
                    resEnum.Value.ToString().Replace("\r\n", "").Replace("\"", "\\\"") + "\"");
            }
 
            sb.Append("}");
            sb.ToString();
 
            context.Response.ContentType = "text/javascript";
            context.Response.Write(sb.ToString());
06.09.2011 09:36 by Danny Gershman | Comments (0) | Permalink

Serializing .NET Resources (.resx) as JSON

This is actually quite a useful solution.  The goal here is to use the same resource file for client and server side coding, while also utilizing the “Culture” awareness of .NET that might be built into your application.

Be sure to create your Resources as a separate satellite assembly. 

We are going to create an HttpHandler.  Basically create a class and implement the IHttpHandler interface.

Look at the sample code below.

    public class ResourceJS : IHttpHandler
    {
 
 
        public bool IsReusable
        {
            get { return true; }
        }
 
        public void ProcessRequest(HttpContext context)
        {
            ResourceManager rm = new ResourceManager(ConfigurationManager.AppSettings["JSResourcesAssemblyType"].ToString(),
                Assembly.LoadFile(ConfigurationManager.AppSettings["JSResourcesAssemblyPath"].ToString()));
            rm.GetString("");
            ResourceSet rs = rm.GetResourceSet(Thread.CurrentThread.CurrentCulture, false, false);              
            var sbInitial = "var rm = {";
            var sb = new StringBuilder(sbInitial);
            var resEnum = rs.GetEnumerator();
            while (resEnum.MoveNext())
            {
                if (sb.ToString() != sbInitial) sb.Append(",");
                sb.Append("\"" + resEnum.Key + "\":\"" + 
                    resEnum.Value.ToString().Replace("\r\n", "").Replace("\"", "\\\"") + "\"");
            }
 
            sb.Append("}");
            sb.ToString();
 
            context.Response.ContentType = "text/javascript";
            context.Response.Write(sb.ToString());
        }
    }

You can see here that there are two Web.config values that have specified here.  JSResourcesAssemblyType is the full Namespace and root class name of the Resource files.  JSResourcesAssemblyPath is the location of satellite assembly.  This uses reflection to load up the metadata of the assembly.

Once this compiled, you need to add this to the web.config.  Here is a sample for IIS7.

 

<handlers>
            <remove name="WebServiceHandlerFactory-Integrated" />
            <remove name="ScriptHandlerFactory" />
            <remove name="ScriptHandlerFactoryAppServices" />
            <remove name="ScriptResource" />            
            <add name="ScriptHandlerFactory" verb="*" path="*.asmx" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
            <add name="ScriptHandlerFactoryAppServices" verb="*" path="*_AppService.axd" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
            <add name="ScriptResource" preCondition="integratedMode" verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
      <add name="ResourceJS" path="ResourceJS.axd" verb="GET" type="ResourceJS" resourceType="Unspecified" requireAccess="Script" preCondition="integratedMode" />
      <add name="UrlRoutingHandler" preCondition="integratedMode" verb="*" path="UrlRouting.axd" type="System.Web.HttpForbiddenHandler, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
    </handlers>

You can see the ResourceJS is being extended to the url ResourceJS.axd.  Below is my sample page.

 

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ResourceJSTest.aspx.cs" Inherits="ResourceJSTest" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <script type="text/javascript" src="js/2.1.0.0-debug/jquery-1.4.4.min.js"></script>
    <script type="text/javascript" src="/ResourceJS.axd"></script>
    <script type="text/javascript">
        $(function () {
            $("#target").html(rm.FAQ_Q8_TEXT);
        });
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div id="target"></div>
    </form>
</body>
</html>
The rm object in javascript has all the “keys” that were defined in the original RESX file for that culture.  You can call any of these as a property of rm.   Very re-usable.
06.08.2011 14:19 by Danny Gershman | Comments (0) | Permalink

PartialView: Why?

There are several cases where you would want to break down your view into several small components. One use case that I am working with right now, is I have a multi-lingual site that I would like to reload content using AJAX principles.

Normally what I would do in the case of a non-multi lingual site is to create another ActionResult to return the ViewModel that is changing with the new parameters. I like to use a custom ActionResult that I have called JsonpResult. The problem resides in the fact that I have labels not in my database but in Resource files. So what I would need to do is to somehow hydrate my Resource file data into the ViewModel.

Once the data comes down the pipe, my AJAX callback handles the wiring up of the ViewModel response back to the HTML page using Javascript (I use jQuery).

This definitely works, however it becomes a question of maintainability. I now need to not only maintain my original ASP.NET view, but I also need to maintain a set of scripts that handle AJAXian behavior. If you need to have your site SEO, then you really need to make sure that both the Server Side and Client Side behavior are both working the same.

This is where Partial Views come into play for me. What I do is "pull out" the logical data sections where the bulk of the reload occurs. The nice thing about PartialView is that you can pass your ViewData and Model along to the PartialView. If your PartialView is strongly typed against your ViewModel you can get Intellisense to help with the wiring of the PartialView.

Now all I need to do with my AJAX call is to write the response back to a single DIV rather than handling data points individually. What it does mean is that there would be more content coming down the pipe. However, the trade off is easier to read and maintain code.

Technorati Tags: ,,,
03.22.2011 20:17 by Danny Gershman | Comments (0) | Permalink

Curvycorners.js with jQuery

Curvycorners.js is a great library that can add curvycorners to DOM elements.  A lot of older browsers don’t support the CSS3 property to add “radius” to make them rounded (amongst other properties).

I thought it might useful to be able to use curvycorners with jQuery.  Here is a very simple example of this.  Enjoy!

 

$(function () {
    var settings = {
        tl: { radius: 10 },
        tr: { radius: 10 },
        antiAlias: true
    }
 
    var divObj = $(".main");
    for (var i = 0; i < divObj.length; i++) {
        curvyCorners(settings, divObj[i]);
    }
});

02.26.2011 19:12 by Danny Gershman | Comments (0) | Permalink

jQuery is bundled w/VS2010

Both webforms and MVC have jquery 1.4.1 in the Scripts folder by default.

image

Technorati Tags: ,,
02.03.2011 11:22 by Danny Gershman | Comments (0) | Permalink

Decoupling CSS Files (Skinning)

It can be very useful to decouple CSS files from each other without reusing classes.  In some cases you might want a designer to focus on handling the coloring items and not have control over positioning.  This can be very painful especially if most of your CSS classes have already been defined.

This methodology allows you to dynamically create some “Skin” classes based off predefined ones.  I have created two simple helper functions.  This tutorial requires that you use jQuery.

function applySkin() {
    _skinObjs($("body"));    
    _skinObjs($("div"));
    _skinObjs($("span"));    
    _skinObjs($("a"));
}
 
function _skinObjs(objs) {
    for (var i = 0; i < objs.length; i++) $(objs[i]).addClass($(objs[i]).attr("class") + "-Skin");    
}

This will essentially, add a “–Skin” class to every body, div, span, and anchor tag on your page (feel free to add additional tags as necessary). 

Now you can remove what information you want repeated.  So let’s say that you have a Site.css stylesheet and you want to remove some stylistic definitions to a separate file (coloring for example).  To continue on that example, take this sample html code.

<html>
<head>
<title>Sample Page</title>
<link href="Site.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
</head>
<body>
<div class="MainTitle">Hello World!</div>
</body>
</html>

 

Take a look at the Site.css, notice how the color information is in the same file.

.MainTitle 
{
    font-size: 32pt;
    position: absolute;
    top: 100px;
    left: 100px;
    background-color: #fea130;
}

So what I need to do now is to create a separate file.  Let’s call it Site-Skin.css.  I will append to my class “-Skin” as seen earlier.  I also need to remove it from my Site.css file.

Updated Site.css

.MainTitle
{
    font-size: 32pt;
    position: absolute;
    top: 100px;
    left: 100px;
}

 

Site-Skin.css

.MainTitle-Skin
{
    background-color: #fea130;
}

Now I need to specify in my HTML code the link to the particular “-Skin” I want to use.  Let’s say that I’m using a scripting language like ASP.NET, I could put the skins in different folders and dynamically change the decoupled CSS file.

<%@ Page Language="C#" %>
<html>
<head>
<title>Sample Page</title>
<link href="Site.css" rel="stylesheet" type="text/css" />
<link href="<%=Skin_Folder%>/Site-Skin.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
</head>
<body>
<div class="MainTitle">Hello World!</div>
</body>
</html>

 

Now if you look below you can see my folder structure has multiple Site-Skin.css files.

image

This is very powerful as your designer can very easily work on the Skin while you work / or someone else works on positioning and other features of the styling process.

Technorati Tags: ,,,
12.14.2010 13:58 by Danny Gershman | Comments (0) | Permalink

Twitter widget changes

Below is the source code for my twitter widget as you see on the top of the page.  This makes a simple JSONP call to Twitter with JQuery.

The change I made today was to read Mentions, Lists, and URLs and convert them to respective URLs. 

 

    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript"></script>
    <script type="text/javascript">
    var last_tweet;
    var twitterAccount = "dannygnj";
    var divClass = "twitterStatus"
    
    $(function() {
        $.getScript("http://twitter.com/statuses/user_timeline/"+twitterAccount+".json?callback=twitterCallback2&amp;;count=1");
    });
    
    function twitterCallback2(data) {    
        last_tweet = twitterize(data[0].text);
        $("." + divClass).html("<img src='/dannyg/pics/twitter_icon.png' style='vertical-align:text-top;'><a href='http://twitter.com/dannygnj' target='_blank'>Last Tweet:</a> " + last_tweet);            
    }
    
    function twitterize(str) {
        var final = str;
        var re = /http:\/\/[A-Za-z0-9_\.\/]*/g;
        var result = final.match(re);    
        if (result != null) {
            for (var i = 0; i < result.length; i++) {
                final = final.replace(result[i], "<a href='" + result[i] + "' target='_blank'>" + result[i] + "</a>");                        
            }
        }
        var re = /@[A-Za-z0-9_]*/g;
        var result = final.match(re);
        if (result != null) {
            for (var i = 0; i < result.length; i++) {
                var xword = result[i].replace("@","");
                final = final.replace(result[i], "@<a href='http://twitter.com/" + xword + "' target='_blank'>" + xword + "</a>");                        
            }
        }
        var re = /#[A-Za-z0-9_]*/g;
        var result = final.match(re);    
        if (result != null) {
            for (var i = 0; i < result.length; i++) {
                var xword = result[i].replace("#","");
                final = final.replace(result[i], "#<a href='http://twitter.com/#search?q=%23" + xword + "' target='_blank'>" + xword + "</a>");                        
            }    
        }
        return final;    
    }    
    </script>     
08.08.2010 10:39 by Danny Gershman | Comments (0) | Permalink

Observer pattern in Javascript

The observer pattern is extremely powerful in many cases where you want to have a Javascript class “listen” to another object.  The class or object attaches to a Subject and waits an update command and then notifies all Observers.    Another key concept here is the idea of inheritance in Javascript. 

I have a simple Javascript function that handles inheritance:

function inherits(base, extension)
{
    for (var property in base)
    {
        try
        {
            extension[property] = base[property];
        }
        catch(warning)
        {
        
        }
    }
}

 

Using this concept in mind I can now extend any additional functionalities of any Javascript class or object on the DOM.  Now let’s take a look at the Observer and Subject classes.

function Observer()
{
    this.Update = function(data)
    {
        alert(this.id + " Update() function has not been implemented");
    }
}
 
function Subject() {
    this.observers = new ArrayList();
}
 
Subject.prototype.Notify = function( context ) {
    var m_count = this.observers.Count();
            
    for( var i = 0; i < m_count; i++ )
        this.observers.GetAt(i).Update( context );
}
 
Subject.prototype.AddObserver = function( observer ) {
    if( !observer.Update )
        throw 'Wrong parameter';
 
    this.observers.Add( observer );
}
 
Subject.prototype.RemoveObserver = function( observer ) {
    if( !observer.Update )
        throw 'Wrong parameter';
   
    this.observers.RemoveAt(this.observers.IndexOf( observer, 0 ));
}

 

We can now go forward and actually implement the code on our HTML page.  Let say we have a series of form fields and we want to set that data equal to the value of another text box.  We could easily subscribe or unsubscribe to listen for updates.  We’ll then use a simple HTML button to send updates to the listening controls.

Our page looks like this in the browser

image

And now for the underlying HTML code.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>    
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
    <script type="text/javascript" src="../Resources/Javascripts/library.js"></script>
    <title>Observer Pattern Example</title>
</head>
<body>
    <form>
        <br />textbox1: <input type="text" id="textbox1" />
        <br />button2: <input type="button" id="button2" />
        <br />notify data: <input type="text" id="textbox3" />
        <input type="button" id="button1" value="Notify" onclick="NotifyObservers(this)" />
    </form>
</body>
</html>
    <script type="text/javascript">
        $(function() {
            inherits(new Observer(), $("#textbox1")[0]);
            inherits(new Observer(), $("#button2")[0]);
            inherits(new Subject(), $("#button1")[0]);
 
            $("#button1")[0].AddObserver($("#textbox1")[0]);
            $("#button1")[0].AddObserver($("#button2")[0]);
 
            $("#textbox1")[0].Update = function(data) {
                this.value = data;
            }
 
            $("#button2")[0].Update = function(data) {
                this.value = data;
            }
        });
 
        function NotifyObservers(subject) {
            subject.Notify($("#textbox3")[0].value);
        }
    </script>

 

As you can see the DOM objects themselves inherit the respective Javascript class functions and properties.  I’m using jQuery to directly call the functions on the objects.  When I fill in some value on the textbox3 that data gets passed through the individual implementations of the Update() method on each object that had been attached to the Subject.

image

The attached objects have decided to update their own values, but in theory I can do anything with that data.  A very powerful design pattern.

This also uses an ArrayList a feature not included in Javascript.  The ArrayList class is seen below.

function ArrayList() {
    this.aList = []; //initialize with an empty array
}
        
ArrayList.prototype.Count = function() {
    return this.aList.length;
}
        
ArrayList.prototype.Add = function( object ) {
    return this.aList.push( object ); //Object are placed at the end of the array
}
 
ArrayList.prototype.GetAt = function( index ) //Index must be a number {
    if( index > -1 && index < this.aList.length )
        return this.aList[index];
    else
        return undefined; //Out of bound array, return undefined
}
        
ArrayList.prototype.Clear = function() {
    this.aList = [];
}
 
ArrayList.prototype.RemoveAt = function ( index ) // index must be a number {
    var m_count = this.aList.length;
            
    if ( m_count > 0 && index > -1 && index < this.aList.length ) 
    {
        switch( index )
        {
            case 0:
                this.aList.shift();
                break;
            case m_count - 1:
                this.aList.pop();
                break;
            default:
                var head   = this.aList.slice( 0, index );
                var tail   = this.aList.slice( index + 1 );
                this.aList = head.concat( tail );
                break;
        }
    }
}
 
ArrayList.prototype.Insert = function ( object, index ) {
    var m_count       = this.aList.length;
    var m_returnValue = -1;
                
    if ( index > -1 && index <= m_count ) 
    {
        switch(index)
        {
            case 0:
                this.aList.unshift(object);
                m_returnValue = 0;
                break;
            case m_count:
                this.aList.push(object);
                m_returnValue = m_count;
                break;
            default:
                var head      = this.aList.slice(0, index - 1);
                var tail      = this.aList.slice(index);
                this.aList    = this.aList.concat(tail.unshift(object));
                m_returnValue = index;
                break;
        }
    }
                
    return m_returnValue;
}
 
ArrayList.prototype.IndexOf = function( object, startIndex ) {
    var m_count       = this.aList.length;
    var m_returnValue = - 1;
                
    if ( startIndex > -1 && startIndex < m_count ) 
    {
        var i = startIndex;
                    
        while( i < m_count )
        {
            if ( this.aList[i] == object )
            {
                m_returnValue = i;
                break;
            }
                        
            i++;
        }
    }
                
    return m_returnValue;
}
        
        
ArrayList.prototype.LastIndexOf = function( object, startIndex ) {
    var m_count       = this.aList.length;
    var m_returnValue = - 1;
                
    if ( startIndex > -1 && startIndex < m_count ) 
    {
        var i = m_count - 1;
                    
        while( i >= startIndex )
        {
            if ( this.aList[i] == object )
            {
                m_returnValue = i;
                break;
            }
                        
            i--;
        }
    }
                
    return m_returnValue;
}

 

Enjoy!  Tweet me if you have any questions @dannygnj

02.18.2010 07:07 by Danny Gershman | Comments (0) | Permalink

JQuery & JSONP

http://jasonkelly.net/archive/2009/02/24/using-jquery-amp-jsonp-for-cross-domain-ajax-with-wcf-services.aspx

Technorati Tags: ,,,
02.08.2010 18:58 by Danny Gershman | Comments (1) | Permalink