Jul 11 2014

Integrate NodeJS tools in Visual Studio/TFS

Category: JavaScript | Asp.netVincenzo @ 03:11

Oops…this is not one of my posts…I invited a friend of mine, Vincenzo, to write about using Node.js tools with Visual Studio. Enjoy!

The presence of NodeJs on TFS online hosted build controller and the recent installation of git client in the platform (suggested by a tweet of mine: https://twitter.com/D3DVincent/status/480968128227074049), and on AppHarbor too (http://support.appharbor.com/discussions/problems/60449-git-binary-in-command-line) opens up new interesting possibilities of integrating Visual Studio and TFS online with the awesome Node JS tools.

In this article I will show you how to set up your Visual Studio and TFS online environment to take advantage of all this.

Note: this is not a NodeJs/tools tutorial. I will assume you have a knowledge about nodejs, npm, bower, tsd, gulp and the packages you're going to install. I will only show you the integration with visual studio.

1) At first, let's make sure to have nodejs installed (www.nodejs.org) with NPM installed and its directories installed into the PATH environment variable.

2) Install a git client (required by bower and tsd) and insert its executables into the PATH environment variable (the current installation package does not set this for you automatically). The most used one on Windows is msysgit (http://msysgit.github.io/), you can find it on WebPlatform too.

clip_image001

clip_image002

clip_image003

Let's make sure that all commands are now available on the command line, by typing its names on the console.

clip_image004

Thanks to this tip, we have now available on Visual Studio console all the tools we need to perform out nodejs tasks.

Prior to see the following steps, I will spend few words on how Npm works.
Unlike nuget, npm has the concept of local and global packages. For this reason, every package installed through this tool can be installed as global (-g flag on the commands). This will make the package available on every directory of your dev machine.

The other option has the same behavior of nuget packages: it will create on your project a new directory called node_modules in which all packages will be stored. You will find a useful .bin directory to all them.

Usually, having packages installed globally is very convenient. In addition, nodejs is smart enough to redirect your commands to the local node_modules directory, if any. This will prevent the usual compatibility issue (more on this, http://blog.nodejs.org/2011/03/23/npm-1-0-global-vs-local-installation)

Let's now open Visual Studio and work with the new tools installed. We will start with a brand new Asp Net empty web application project.
Unfortunately the package manager console looks like unable to handle interactive commands. So we will have to switch on command line some time.
Go to the package manager console and type this command

npm init

The command will ask us few questions about our new webapplication and write a package.json file for us.

clip_image006

This json file will track all installed package and will be used as a source for restoring packages during a build.

Visual Studio does know nothing about package.json, since we're using an external tool. This file must be included in our source control. I prefer to keep it in the solution too, but this is up to you. Let's go to the solution explorer, show all files and include in solution the package.json file

clip_image007

Now we have npm installed with a repository of over 80 thousand packages. You can browse all of them here: https://www.npmjs.org/.

For a modern web application, I always install these packages from npm repository:

1)Bower (package manager for javascript libraries)

2)Tsd (typescript definitions for javascript libraries)

3)GulpJS (task and automation tools)

npm install bower --save

npm install tsd --save

npm install gulp --save

The --save flag instructs npm to write this installation to packages.json file. If all was done correctly, our file should look like this:

clip_image008

Thanks to these entries, we will be able to perform a package restore on TFS and on our dev machines too.

Since I do not have global packages installed, I have to point the bin directory of node_modules folder.

clip_image010

Let's init tsd too:

clip_image012

And do not forget to include json files into solution.

clip_image013

We're done with interactive commands. We can close command prompt and work comfortably from visual studio and its package manager console.

Next step are just to install packages and tools: I will take jquery and angularjs from bower, their typings from tsd and set up a simple minify task with gulp:

clip_image015

The installed components are, obviously, external to Visual Studio. We have to include them into the project:

clip_image016

In my example, I will include only the highlighted voices. Anyway, the strategy is up to you. If you will select these files, they will be published on Azure or your WebDeployment enabled server. If not, you will have to extend the MsBuild task to include these files.

Now we have a more or less complete development solutions. Install other packages and code your solutions.

Question from .NET developers: why do not use nuget package manager to install DefinitelyTyped Typescript definitions? Well, actually nuget is not able to restore content folder from its packages and developers are not going to cover this use case (you can find more about this issue and why it won’t be fixed here: http://nuget.codeplex.com/workitem/2094. Due to this, we are forced to include into our checkin typings file too. Even if a lot of persons suggests that this is the right thing to do, I do not like this kind of procedure. Switching to the cliend side, I can remove and restore these kind of packages as well and keep under source control only MY app files.

Done that, is time for automation.

At first, let's configure exclusions for TFS source control. We have to add a new .tfignore file in root of our solution and add the following lines:

\NodeJsTools\bower_components
\NodeJsTools\typings

This will exclude these folders from source control, even if they are included into solution file. We do not need to add node_modules since it's completely external.

This means that our hosted source code will miss all nodejs modules and all bower/tsd reference files. In this way, our application will never work (and won't pass the build, since the Typescript target installed into tfs controller will fail: it won't find definitions for angular and jquery).

Here is where MsBuild can help us. We will inject our dependencies task directly on BeforeBuild target.
Tfs controller have got nodejs installed (and npm too) with a git client (needed for bower).

Let's open our project file (.csproj) and let's paste this.

  <Target Name="BeforeBuild">
    <Exec Command="npm install" />
    <Exec Command="node_modules\.bin\tsd reinstall" />
    <Exec Command="node_modules\.bin\bower install --config.interactive=false" />
  </Target>

This target, which will run before build, will install all modules listed into packages.json, then all definitions in tsd.json and all the ones of bower.json. In this way, all references will be fine and the build will pass. Note that I'm using local packages in node_modules since I cannot assume that packages will be installed on tfs hosted build controller.
Let's add these lines too:

  <Target Name="AfterBuild">
    <Exec Command="node_modules\.bin\gulp $(Configuration)" />
  </Target>

These will run my gulp task depending on configuration. In Release, for example, I use to do angular template cache, html and js minification and things like this. In Debug, I usually run jasmine tests. The decision is up to you.

Let's now extend the Clean target to delete also all nodejs stuff. Insert into a PropertyGroup this tag:

     <CleanDependsOn>
        $(CleanDependsOn);
        CleanNodeFiles;
    </CleanDependsOn>

And then let's define this target:

<Target Name="CleanNodeFiles">
      <ItemGroup>
        <TypeScriptGenerated Include="%(TypeScriptCompile.RelativeDir)%(TypeScriptCompile.Filename).js" Condition="!$([System.String]::new('%(TypeScriptCompile.Filename)').EndsWith('.d'))" />
        <TypeScriptGenerated Include="%(TypeScriptCompile.RelativeDir)%(TypeScriptCompile.Filename).js.map" Condition="!$([System.String]::new('%(TypeScriptCompile.Filename)').EndsWith('.d'))" />
    </ItemGroup>

    <Delete Files="@(TypeScriptGenerated)" />
    <RemoveDir Directories=".\build\;.\bower_components\;.\typings\"/>
    <Exec Command=".\tools\DelFolder .\node_modules" />
  </Target>

These lines will delete all typescript generated files and all bower/tsd files.
You may see that node_modules directories and subdirectories are deleted using a DelFolder script and not through the remove dir. This is because node_modules are very deep directory and RemoveDir target cannot delete paths longer that 256 characters. For this reason, I use to include a script in my source control (not in the project, however) with these lines:

@echo off
if \{%1\}==\{\} @echo Syntax: DelFolder FolderPath&goto :EOF
if not exist %1 @echo Syntax: DelFolder FolderPath - %1 NOT found.&goto :EOF
setlocal
set folder=%1
set MT="%TEMP%\DelFolder_%RANDOM%"
MD %MT%
RoboCopy %MT% %folder% /MIR
RD /S /Q %MT%
RD /S /Q %folder%
endlocal

As final task, we may want to add custom files (generated by gulp js tasks for example). We can do it with other few lines of msbuild code:

<Target Name="DefineCustomFiles">
    <ItemGroup>
      <CustomFilesToInclude Include=".\build\**\*.*">
        <Dir>./</Dir>
      </CustomFilesToInclude>
    </ItemGroup>
  </Target>

  <Target Name="CustomCollectFiles">
    <ItemGroup>
      <FilesForPackagingFromProject Include="@(CustomFilesToInclude)">
        <DestinationRelativePath>%(CustomFilesToInclude.Dir)\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
      </FilesForPackagingFromProject>
    </ItemGroup>
  </Target>

These lines instruct the deployment to include all build folder and place it into the root.
A typical use case is this one:
Usually you have got your html files into solutions and using gulp, you minify your views. Of course you will not save these changes into your project root, or your source files will be modified and not usable (since they are minified).
For this reason, you may want usually to place all modified files into another folder, (in my case, build). Of course, Visual Studio does know nothing about these files since they are completely external to your webapplication. Thanks to this task, we will include these files into WebDeployment and place it into root folder, replacing the "development" files.
This task can be useful to include into deployment the bower_components folder (selecting the appropriate scripts) and removing from your .csproj all the folder reference. This choice is up to you. However, I usually prefer to keep scripts reference into solutions to always have a clear overview of what my webapplication needs to run up.

Ok, let's casting out nines: clean your project and verify that all nodeJs folders are gone. It may take a bit since the robocopy script is slower than a simple directory delete. Then build your project and all the stuff should be in its place.

You can find all the source code for this demo cloning this repository: https://github.com/XVincentX/NodeVStudio
Good. We're done. Have fun.

 

P.S: The project is evolving: have a look here -> https://github.com/XVincentX/NodeJsMsBuild
P.S2: I was mentioned in Morning Brew and Asp.Net site too!

image image

Tags: , ,

Apr 3 2012

Calling MVC Actions from Javascript

Category: MVC | Asp.netVincenzo @ 07:57

Oops…this is not one of my posts…but a friend of mine that is the coordinator of the jsAction team asked me to write a post on my Blog since he has not yes his own Blog. He promised me to enhance jsAction to make easier the use of my updateManager js class….

                                                              Francesco

Suppose we have a very simple action method: it takes no parameters and just returns a string:

public string MyTestMethod()
{
     return "Test String";
}

What if we would like to call this action method from javascript through Ajax and to display the value it returns?
We can do it with jQuery:

function AjaxDisplayString()
{
   $.ajax({
        url: @Url.Action("MyTestMethod"),
        method: 'GET',
        success: function(data) { alert(data); }
   });
}

Not so bad, after all. However, let think, about all implications of the above simple raw approach to the interaction with an action method:

  1. Repeating the above code several times in an application is annoying, and error-prone.
  2. We have no access to Razor helpers from within js files, so we can’t use @Url.Action, and other similar methods to compute the Url we need.
  3. The code depends on the action method being marked  as HttpPost or HttpGet. Each time we modify these attributes we have to go through all our js files that might contain references to the action method and change them.


Are there a better ways to interact with an action methods that doesn’t suffer of the above problems?
….There are some:

  1. We can pack all information that define the details of the call into a standard option object and we may simply write something like:
    $.ajax(options);
  2. There are extensions, like RazorJs, that enable us to write Razor sintax into Js files (http://john.katsiotis.com/blog/razorjs---write-razor-inside-your-javascript-files)
  3. ….Actually problem 3. this is not easy to solve….


However, is there a way to solve all the above problems together?
….This lead me to conceive JsAction.
Well, now that the we made the problem clear, we are ready to go thorugh JsAction and see how it can help us.

JsAction can be installed in 2 ways:

  1. By downloading the library from download page and by referencing it in Visual Studio
  2. By using the NuGet package manager and adding the JsAction package.
    Go to Tools -> Library Package Manager -> Add Library Package Reference and select OnLine. Once located the package press install.
 

You just need to add script references  to the pages that use it. This is easily accomplished by placing the reference into the layout pages (usually your layout.cshtml).
It must be called after the jQuery reference:

@using JsAction

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
    <script src="../../Scripts/jquery-1.7.1.min.js" type="text/javascript"></script>
    <script src="../../Scripts/jquery.unobtrusive-ajax.min.js" type="text/javascript"></script>
    @Html.JsAction()
    <script src="../../Scripts/script.js" type="text/javascript"></script>
</head>

<body>
    @RenderBody()
</body>
</html>

NOTE: script.js is not a part of the libray but just the example javascript file that needs to call the action method.
Clearly we must place it after the jsAction reference.

Simple Usage

Once configuration is done, we can use JsAction.
Let's take the usual Home controller class and let decorate the methods we would like to call from the client with JsAction attribute:

        [JsAction()]
        public string MyTestMethod(int a, int b)
        {
            return (a+b).ToString();
        }

For sake of simplicity let's use just the default parameterless costructor, further options can be found in the documentation
JsAction  creates a JsActions object containing all methods we marked with the JsAction attribute.
Now we are ready to use them from Javascript code with the pattern JsAction.ControllerName.Method:

$(document).ready(function () {
    var ret = JsActions.Home.MyTestMethod(1,2);
});

Where ret is the object returned by the $.ajax call.

According to the jQuery documentation:
The jQuery XMLHttpRequest (jqXHR) object returned by $.ajax() as of jQuery 1.5 is a superset of the browser's native XMLHttpRequest object.
and
The jqXHR objects returned by $.ajax() as of jQuery 1.5 implement the Promise interface

This means, if we are using jQuery 1.5+, we can take advantage of the Promise interface:

$(document).ready(function () {
     JsActions.Home.MyTestMethod(1,2).then(function(data){ alert(data); });
});

What if i'm using jQuery < 1.5?
Well, …we can still set all usual jQuery ajax call options, since all methods created by JsAction takes an optional option parameter, that is merged with the jQuery ajax call options(see the jQuery ajax documentation –> http://api.jquery.com/jQuery.ajax/ ),  Thus, our previous example becomes:

$(document).ready(function () {
    var ret = JsActions.Home.MyTestMethod(1, 2, {
        success: function (data) {
            alert(data);
        }
    });
});

Summing up JsAction provide a quick and easy way to map  server side methods into client side methods in  1:1 fahion.

Visual Studio provides a smart intellisense support not only for .NET languages (C#, Vb.NET), but also for JavaScript (see the new ide features http://weblogs.asp.net/scottgu/archive/2010/04/08/javascript-intellisense-improvements-with-vs-2010.aspx ).

The good news is that jSAction not only imports in javascript the action methods, but also the the help encoded into the action methods comments:
Let's  experiment this by writing a simple class and an action method marked with the JsAction attribute…. and by adding some server side “help comments” on this action method:

    public class Student
    {
        public string firstName { get; set; }
        public string lastName { get; set; }
    }

        /// <summary>
        /// Sums two numbers
        /// </summary>
        /// <param name="st">The student</param>
        /// <param name="a">First number</param>
        /// <param name="b">Second number</param>
        /// <returns>The sum</returns>
        [JsAction()]
        public JsonResult MyTestMethod(Student st, int a, int b)
        {
            st.SumHere = a + b;
            return Json(st);
        }

1.png
Now let run the application and let verify carefully that it actually compile and runs, since the Doc generation feature requires that

  • The application compiles.
  • The application runs.
  • The Xml output file is enabled (Project Properties -> Build -> Check "Xml Documentation File").


Now we're ready to generate automatically the javascript documentation. Go to Tools->Library Package Manager-> Package Manager Console
2.png
Once opened we just have to write
PM> JsAction-GenDoc
The project will be built and run. If any error occurs, it will be displayed in the console. 
Please just wait without interacting with the consolle until the project close automatically. Then lets go to the Solution Explorer and let give a look to the script folder
3.PNG
A new file has been added: JsAction documentation file. It contains unminified and commented javascript code.

/*Generated: 19/02/2012 23:50:35*/
var JsActions = {
    MyTestMethod: function (st, a, b, options) { ///<summary>
        ///            Sums two numbers
        ///            </summary>
///<param name="st" type="Student">The student</param>
///<param name="a" type="Int32">First number</param>
///<param name="b" type="Int32">Second number</param>
///<returns>The sum</returns>
///<param name="options" type="ajaxSettings">[OPTIONAL] AjaxOptions partial object; it will be mergend with the one sent to .ajax jQuery function</param> var opts = { url: "/Home/MyTestMethod", async: true, cache: true, type: "GET", data: $.toDictionary({ st: st, a: a, b: b }) }; jQuery.extend(opts, options); return jQuery.ajax(opts); } };

Now let's create a new javascript file and use the doc file as reference.
Code completion
4.png
Code comments!!
5.png
As you can see from the second image, also the parameter types are resolved. (Student,Int32). This should help you to never use a wrong type.

The Intellisense feature will work also if no Xml Documentation file generation is enabled. However, it will miss YOUR comments, providing only method signature and type resolution.

Please Notice, the vsdoc file must never be used as a @JsScript call replacement. It misses internal functions and it's purpose it's only to documentate function during development.

JsAction has also a basic WebApi support.
Below an ApiController decorated with the JsAction attribute

[JsAction()]
 public class StudentController : ApiController
 {
     private List<Student> data;

     public StudentController()
     {
         this.data = new List<Student>()
         {
             new Student(){id=0, Name="Vincenzo", Surname="Chianese", BirthDay=DateTime.Parse("20/05/1989"), Exams=10},
             new Student(){id=1, Name="Fernando", Surname="Alonso", BirthDay=DateTime.Parse("19/07/1981"),Exams=0},
             new Student(){id=2, Name="Bill", Surname="Gates", BirthDay=DateTime.Parse("28/10/1955"), Exams=2}
         };

     }
     public IEnumerable<Student> GetStudentList()
     {
         return data;
     }

     public Student GetById(int id)
     {
         return data.Where(s => s.id == id).First();
     }

     public Student GetByName(string name)
     {
         return data.Where(s => s.Name == name).First();

     }

     public Student GetBySurname(string surname)
     {
         return data.Where(s => s.Surname == surname).First();
     }

     public HttpResponseMessage PostStudent(Student st)
     {
         //new student logic
         return new HttpResponseMessage(System.Net.HttpStatusCode.OK);
     }

     public HttpResponseMessage DeleteStudent(int id)
     {
         var elem = data.Where(q => q.id == id);
         if (elem.Count() > 0)
         {
             //Remove logic
             return new HttpResponseMessage(System.Net.HttpStatusCode.OK);
         }
         return new HttpResponseMessage(System.Net.HttpStatusCode.NotFound);
     }
 }

The above controller implements just a simple CRUD logic. As you can see the JsAction attribute is applied to the whole class instead of on the Action Methods. In fact in thie case of ApiControllers, substantially, all methods are created to be called from javascript, so it make no sense to decorate only some methods.
 
Anyway, since all methods decorated with NonActionAttribute are not intended to be exposed on the Web no javascript method is created for calling them.

All WebApi methods are placed into the JsActions.WebApi namespace. Below the unit test results of the first controller, and the an example of usage:

test('JsAction WebApi support', function () {
    stop();
    JsActions.WebApi.Student.GetStudentList().then(function (data) { ok(typeof data != 'undefined', 'WebApi data retrieving'); start(); });
    stop();
    JsActions.WebApi.Student.GetById(2).then(function (data) { ok(typeof data != 'undefined', 'WebApi data retrieving 2'); start(); });
    stop();
    JsActions.WebApi.Student.PostStudent({ id: 3, Name: "Francisco", Surname: "Franco", BirthDay: new Date(), Exams: 15 }, { statusCode: { 200: function () { ok(true, 'New element inserted'); start(); } } });
    stop();
    JsActions.WebApi.Student.DeleteStudent(500, { statusCode: { 200: function () { ok(false, 'This is not good'); start(); }, 404: function () { ok(true, 'Element not found'); start(); } } });

 

image

JsAction, when needed, performs also a Complex Type Decomposition:.

image

As you can see from the image image above, if a method  has complex types as parameters, then JsAction creates  javascript proxies for the with full intellisense support.

                      That’s all for now……more to come

                                    Vincenzo

Tags: , ,