65.9K
CodeProject is changing. Read more.
Home

Optimal and unobtrusive JavaScript arrangement in MVC project

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2 votes)

Mar 25, 2011

CPOL

1 min read

viewsIcon

22441

Optimal and unobtrusive JavaScript arrangement in MVC project

Since I started coding in MVC3, I had to work my way in calling proper JavaScript functions in given views. Through some ideas, I came to this "pattern". I utilize jQuery to get DOM elements and bind events.

Following Douglas Crockford's tips, I encapsulate all my JavaScript functions inside a container function (for more on 'why is this better' Google 'Douglas Crockford'). Then in the document.ready event, I initialize it and the magic begins :)

So, in my scripts.js file (later minified and possibly obfuscated), I start with something like this:

function SiteModule(window, viewData, undefined) {
    var that = this;

    function somePrivateFunction() {
        //inside private finction
    }

    this.Home_Index = function (window, viewData, undefined) {
        //inside public function
    }

    this.About_Index = function (window, viewData, undefined) {
        //inside public function
    }

    $(".js-module").each(function () {
        var module = $(this).attr('data-module');
        try {
            return new that[module](window, viewData);
        } catch (e) {
            throw new Error('Unable to create module "' + module + '"');
        }
    });
}

var _viewData = {};
var _site = null;

$(function () {
    _site = SiteModule(window, _viewData);
});

In _Layout.cshtml (since I'm using Razor, but I guess it can be adapded to other view engines), I put:

<script src="@Url.Content("~/Scripts/jquery.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/scripts.js")" type="text/javascript"></script>
<script type="text/javascript">
    _viewData = {
        @RenderSection("scriptViewData", false)
        someOtherProperty: '@SomeModelValue'
    };
</script>

In the above snippet, I render the scriptViewData section which gives me the ability to pass data from the rendered view to the script, like this:

@section scriptViewData{
   someProperty : @ViewBag.SomePropertyIWantToPassToJS,
   someOtherProperty : @Model.SomeOtherPropertyIWantToPassToJS,
}

Notice the comma at the end of each property line? That's because this is just a part of the _viewData object, and in the _Layout view, after the RenderSection call, I add some other useful properties to keep the object valid.

And finally, in the Views (partial, non-partial), wherever I want, I add:

<span class="js-module" data-module="Home_Index"></span>

js-module is a class name used by the module to get the element. I'm using class and not some data-... attribute because jQuery's class selector is faster than the attribute selector. Also, in your CSS, you can write something like .js-module {display: none}.

And data-module is the name of the JavaScript public function defined in the SiteModule function.