Home > .NET, JQuery, MVC, SPA, Vs2013 > RequireJS with Visual Studio 2013 & WebAPI

RequireJS with Visual Studio 2013 & WebAPI

2014/01/20

Now that the SPA is the mainstream technology of the moment, i was wondering how really are useful Knockout, Durandal, RequireJS and so on.

These new buzzwords are javascript frameworks, founded (RequireIS no) on the well knowed jQuery.

I have done many web applications with jQuery and SPA concepts , using web forms (.aspx) as html/json generators and then decoding/evaluating the resulted html or JSON by hand.

So i started a lot of tests, trying to convert some old web applications in “à la page” SPA apps.

This is not a simple task , because the technologies are rapidly changing and evolving (as the jQuery versions): so the sample probably working at the time of the publications three months after is not working because jQuery changes, new library versions and so on.

I tried to evaluate RequireJs, the first step was to create with Visual Studio 2013 a new ASP.NET Web Application, using the WebAPI template:


Note that i explicity chosen Web API.

Then the first thing that seems obvious is to add a WebAPI controller:


naming it “TestController”

The generated TestController.cs contains

namespace RequireJsTest.Controllers
{
    public class TestController : ApiController
    {
        // GET api/<controller>
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }

        // GET api/<controller>/5
        public string Get(int id)
        {
            return "hi this is the test " + id.ToString();
        }
........


In this case i changed the default “value” string with “hi this is the test ” + id.ToString().

By adding the WebAPI there is a new class WebApiConfig in App_Start, called in the global.asax.

Note the comment above // GET api/<controller>/5 : this will be be the path for the call, with the template defined in the WebApiConfig class.

If you try http://localhost:<yourport>/api/test/5 you get a file named 5.json , that contains “hi this is the test 5” (using the default Visual Studio web server).

Now, in the past in a complex web application with many .js sources it was requested a manual and complex management in order to provide the requested js modules in a certain situation, for example we are deleting a db record and it is necessary to provide the infrastructure for the ajax call vs the db and the messaging framework to the user.

With RequireJS the dependencies management in javascript is simplified, the pages http://requirejs.org/docs/start.html and http://requirejs.org/docs/api.html#data-main explains very well the concepts.

So we can install RequireJS from TOOLS->Library Package Manager->Manage NuGet Packages for Solution…

and search online:


Done this , to the HomeController.cs we can add a new method

public ActionResult Test()
{
    ViewBag.Title = "Test Page";
    return View();
}

Right clicking this method we can Add View for this Controller method,


That creates an Test.cshtml file under Views/Home :


By default there is only an <h2> , we change to

@{
    ViewBag.Title = "Test";
}

<h2>@ViewBag.Title.</h2>
<h3>@ViewBag.Message</h3>

<div id="msgplaceholder"></div>
<script data-main="/app/main" src="@Url.Content("~/Scripts/require.js")"></script>

Require.js is the script added from RequireJS, and now we see what is the “data-main” attribute.

A SPA app typically uses a folder named “app” , immediately under the root, as the main placeholder of the SPA structure.

And another commonly used standard is to use a main.js file as the SPA core.

As the requireJS site write, “The data-main attribute is a special attribute that require.js will check to start script loading”.

So the last <script> loads a file named main.js using the require.js library.

At this point we need to create the main.js , and a structure for the separation of the concepts.

In this case:


In main.js we define some aliases for other js modules, specifying the path:

(function () {
    requirejs.config(
        {
            paths: {
                'testview': '/app/viewmodels/test',
                'datasvc': '/app/services/testsvc',
                'config': '/app/configs/config'
            }
        }
    );

    require(['testview'],
        function (testvw) {
            //debugger;
            var id = 10;
            testvw.showMessage(id);
        }
    );
})();

Here the function decorated with require([‘testview’] is automatically executed, if you place another function for example

require(
    ['testview'],
    function (testvw) {
        //debugger;
        var id = 10;
        testvw.showMessage(id);
    }
);

require(
    ['testview'],
    function (testvw) {
        var id = 20;
        testvw.showMessage(id);
    }
);

is automatically executed after the first has finished (the result depend from the callbacks timings).

Note that the function define as required the module that we aliased with ‘testview’, that points to /app/viewmodels/test.js

Here in test.is we can write

define('testview',
    ['jquery', 'datasvc'],
    function ($, datasvc) {
        var showMessage = function (id) {
            datasvc.getTest(id, function (message) {
                $("#msgplaceholder").html(message);
            });
        };

        return {
            showMessage: showMessage
        };
    }
);

That is we define that the current module will be publicy knowed as “testview” with “define”, and so in main.js we can write “require([testview]”

In test.js we note that this module is defined with the public name “testview”, and internally requires jQuery , which is already publicy knowed with the $ symbol, and our other module “datasvc”,

Here we can define some functions, the ones that we need public are defined public with the “return” keyword (a sample of Module Pattern).

We know now that datasvc is the alias defined in /app/services/testsvc.js where we can write something as

define('datasvc',
    ['jquery', 'config'],
    function ($, config) {
           var callWebApi = function (url, type, callbackfunc) {
                //debugger;
                $.ajax({
                    url: url,
                    type: type,
                    dataType: 'json',
                    success: function (data) {
                        callbackfunc(data);
                    },
                    error: function (jqXHR, textStatus, errorThrown) {
                        callbackfunc(jqXHR.responseText);
                    }
                });
            },

            getTest = function (id, callback) {
                url = config.baseUrl + id;
                callWebApi(url, 'GET', callback);
            };

        return {
            getTest: getTest
        };
    }
);

That requires “config” defined in /app/configs/config.js

define('config',
    [],
    function () {
        var baseUrl = '/api/test/';  //important ! Verify that the name must be the same of the controller

        return {
            baseUrl: baseUrl
        };
    }
);

Note that config.js is not requiring other libraries, so we can write define(‘config’, []

That is nothing in the square brackets.

if we try to run our app and point the browser to /home/test ( for example http://localhost:29404/home/test)


Conclusion: effectively some of these new frameworks are really useful and RequireIS leads to a less “spaghetti” javascript code.

Advertisements
Categories: .NET, JQuery, MVC, SPA, Vs2013
%d bloggers like this: