TypeScript : Using Backbone.Marionette and REST WebAPI (Part 1)

This article ( Part 1 and Part 2) aims to walk the reader through setting up a Backbone.Marionette SPA application with Visual Studio 2013, and in particular, write Marionette JavaScript using TypeScript.  Generally, writing JavaScript applications relies on RESTful web services, so this blog will also aim to show how to accomplish this with ASP.NET WebAPI data controllers.

Backbone.Marionette was designed to simplify large scale JavaScript applications.  After a mate of mine ( three votes, two votes, two votes one. ) loaned me a book on Marionette, I decided to give it a whirl.

Being the unit-test junkie that I am, I also aim to show how to build JasmineJs unit-tests to test your REST services, and also show how to generate and use nested JSON objects.  Nested JSON data fits hand in glove with Backbone Models and TypeScript.

Update : Many thanks to Alastair who pointed out some typos. These have now been fixed.

Firstly, a few links:

Backbone.Marionette.js: A gentle introduction is an excellent book by David Sulc, that I used to gently introduce me to Marionette. 

Marionettejs.com is the official Marionette site – downloads and documentation.

MarionetteJS 1.4.1 on nuget is the nuGet repository by benb1in.

marionette.TypeScript.DefinitelyTyped is the nuGet repository for TypeScript definitions by Jason Jarrett

DefinitelyTyped/marionette holds the TypeScript definition .d.ts files for Marionette, by sventschui.

Twitter.Bootstrap is the nuGet repository for Bootstrap 3.0.1.1 by Jakob Thornton and Mark Otto

bootstrap.TypeScript.DefinitelyTyped is the nuGet repository for TypeScript definitions of boostrap by Jason Jarrett

As always, the full source code for this article can be found on github: blorkfish/typescript-marionette

I have broken this article up over two parts, as we will be covering quite a few techniques and sample code – but fear not, it is quite fast-paced with tangible results every step of the way.  As an overview, we will accomplish the following:

  • Setting up a Visual Studio 2013 project.
  • Install required nuGet packages.
  • Creating the ASP.NET HomeController and View.
  • Including required javascript libraries in our Index.cshtml.
  • Creating a Marionette.Application.
  • Adding a Marionette Region
  • Referencing the Region in the Application
  • Creating a Marionette.View
  • Using bootstrap to create a clickable NavBar Button
  • Using Backbone Models to drive NavBar buttons.
  • Creating a Backbone.Collection to hold multiple Models
  • Creating a Marionette.CompositeView to render collections
  • Rendering Model Properties in templates
  • Using Marionette Events.
    At the end of Part 1, our awesome application will look like this : and use events to notify our App when a navbar button is clicked:

image

image

In Part 2 of this article we will cover the following:

    • Creating an ASP.NET WebAPI Data Controller.
    • Unit testing the WebAPI Data Controller in C#
    • Modifying the WebAPI Data Controller to return a collection of nested C# POCO objects.
    • Defining TypeScript Backbone.Model classes to match our nested class structure.
    • Writing Jasmine unit tests for our Backbone Collection.
    • Creating a Marionette.CompositeView to render data in a bootstrap table.
    • Using a Marionette.CompositeView as an ItemView
    • Rendering nested Backbone.Collections
    • Using CompositeView properties to generate html.
    • Using bootstrap styles in a Marionette.CompositeView
    When we are complete with this tutorial, we will have generated a nested Json structure as follows: UserList –> User –> RoundScores –> RoundScore.
    We will then render it as follows:

image

 

Setting up a Visual Studio 2013 project.

Firstly, let’s create a new Visual Studio project.  Make sure that you select an ASP.NET MVC 4 Web Application.  This will allow for the addition of WebAPI data controllers.

image

Personally, I prefer creating an Empty web application – as Visual Studio makes it very easy to add Controllers and Views later.  Change the View Engine to Razor.

image

Next, let’s switch to the .NET framework 4.5.1.  Right-click on the project and choose Properties.  Under Application, change the Target framework to .NET Framework 4.5.1:

image

Install required nuGet packages.

Next, install the following nuGet packages.  Click on TOOLS | Library Package Manager | Package Manager Console, and type the following:

Install-Package Backbone.Marionette

Install-Package marionette.TypeScript.DefinitelyTyped

Install-Package jasmine-js

Install-Package jasmine.TypeScript.DefinitelyTyped

Install-Package Twitter.Bootstrap

Install-Package bootstrap.TypeScript.DefinitelyTyped

Install-Package jasmine-jquery.TypeScript.DefinitelyTyped

Install-Package json2

Install-Package Newtonsoft.Json

Install-Package Microsoft.AspNet.WebApi

Install-Package underscore.TypeScript.DefinitelyTyped

NOTE : Compiling the project now will generate about 120 compile errors, similar to the following:

Build: Duplicate identifier ‘abort’. Additional locations

To fix this, navigate to the Scripts / typings / jasmine directory, and delete the jasmine-1.3.d.ts file from the project.

Creating the ASP.NET Home Controller and View.

Next, we will need to create a Controller and View to serve up a simple web page.

From Solution Explorer, right-click on the Controllers directory, and select Add | Controller.  Call it HomeController, and use the Empty MVC Controler Template:

image

Now create a Home directory under Views – and then right-click on the Home directory, and select Add | View .  Name the View “Index” as below:

image

Our solution explorer should look as follows:

image

Modify the Index.cshtml with some Hello world text as follows – just to check that we can hit this view:

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    <div>
        <h1>Hello Home Controller</h1>
    </div>
</body>
</html>

Running the application now ( hit F5 ) should successfully run the HomeController, and serve our Index.cshtml page:

image

Including required javascript libraries in our Index.cshtml.

Our next step is to reference some of the javascript libaries that we downloaded via nuGet in order for Marionette to work correctly.  Note that nuGet has placed the downloaded javascript libaries in the folder /Scripts – and the downloaded TypeScript definition files in /Scripts/typings.

Also, when running with Internet Explorer, we will need to ensure that IE uses the correct engine when parsing both the DOM and JavaScript.  This is accomplished by adding a meta tag to the page to force IE to use the latest JavaScript engine (by default, IE will revert to the IE 8 engine).

Modify the Index.cshtml page to include the following javascript libaries – and add the meta tag for IE as follows:

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Index</title>

    <link rel="stylesheet" href="../../Content/bootstrap.css" type="text/css" />

    <script language="javascript" type="text/javascript" src="../../Scripts/jquery-1.9.1.js"></script>
    <script language="javascript" type="text/javascript" src="../../Scripts/json2.js"></script>
    <script language="javascript" type="text/javascript" src="../../Scripts/underscore.js"></script>
    <script language="javascript" type="text/javascript" src="../../Scripts/backbone.js"></script>
    <script language="javascript" type="text/javascript" src="../../Scripts/backbone.marionette.js"></script>
    <script language="javascript" type="text/javascript" src="../../Scripts/bootstrap.js"></script>
    
</head>
<body>
    <div>
        <h1>Hello Home Controller</h1>
    </div>
</body>
</html>

Creating a Marionette.Application

All Marionette SPAs start with a Marionette.Application.  The Marionette.Application has a number of responsibilities, including responding to application-wide events, and defining broad html “regions” that relate to specific application controllers and views.

To create a Marionette Application, simply create a class that extends from Marionette.Application.

This is one of the greatest advantages of using Backbone and Marionette with TypeScript.  The TypeScript extends keyword does exactly what you would expect it to do – it extends the definition of an existing class.  Just what you would expect from an Object Oriented language.  Unfortunately, most of the popular JavaScript libraries are not built this way, and use configuration settings to drive object behaviour.  Coming from a strongly typed background, I personally find it easier to understand and work with Backbone and Marionette because of this.

I plan to write a blog fairly soon comparing how Backbone, ExtJs, AngularJs and Marionette shape up as compatible libraries when using TypeScript and Visual Studio as your main development tools.  In the  meantime, though, Marionette wins the race hands-down (IMHO).

So back to the task at hand.  I prefer to keep all TypeScript code in a separate directory, named /tscode. Go ahead and create this directory, and then create a new TypeScript file in the /tscode directory named MarionetteApp.ts:

Add | New Item | Visual C# | Web | TypeScript File:

/// <reference path="../Scripts/typings/jquery/jquery.d.ts"/>
/// <reference path="../Scripts/typings/underscore/underscore.d.ts"/>
/// <reference path="../Scripts/typings/backbone/backbone.d.ts"/>
/// <reference path="../Scripts/typings/marionette/marionette.d.ts"/>

class MarionetteApp extends Marionette.Application {
    constructor() {
        super();
        this.on("initialize:after", this.initializeAfter);
    }
    initializeAfter() {
        alert("initializeAfter called");
    }
}

Now, lets include this file in the Index.cshtml.  Note that to start-up a Marionette application, we need to instantiate an instance of our Marionette.Application, and call the start() function:

Modify your index.cshtml file – firstly to include the MarionettApp.js file in the <head> element, and then with a script down the bottom to instatiate the application, and call start() :

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Index</title>

    <link rel="stylesheet" href="../../Content/bootstrap.css" type="text/css" />

    <script language="javascript" type="text/javascript" src="../../Scripts/jquery-1.9.1.js"></script>
    <script language="javascript" type="text/javascript" src="../../Scripts/json2.js"></script>
    <script language="javascript" type="text/javascript" src="../../Scripts/underscore.js"></script>
    <script language="javascript" type="text/javascript" src="../../Scripts/backbone.js"></script>
    <script language="javascript" type="text/javascript" src="../../Scripts/backbone.marionette.js"></script>
    <script language="javascript" type="text/javascript" src="../../Scripts/bootstrap.js"></script>
    
    <script language="javascript" type="text/javascript" src="../../tscode/MarionetteApp.js"></script>
</head>
    <body>
        <div>
            <h1>Hello Home Controller</h1>
        </div>

        <script type="text/javascript">
            var marionetteApp = new MarionetteApp();
            marionetteApp.start();
        </script>
    </body>
</html>

Running the web application now should call the alert() function :

image

Adding a Marionette Region

As mentioned before, one of the responsibilities of  a Marionette Application is to create and manage regions.  Think of a region as a broad section of your html that Marionette will inject DOM elements into.  These regions can be shown and hidden – and can even transition with JQuery animations.  To create a Region, simply add a div to your html page, and specify an id.   As we have already installed bootstrap with nuGet, let’s use it to create a navbar panel with a region in it. 

Modify your Index.cshtml as follows:

 <body>
        
        <div class="navbar navbar-inverse navbar-fixed-top">
            <div class="container">
                <div class="row">
                    <div class="col-lg-6">
                        <div >TypeScript Marionette</div>
                    </div>
                    <div class="col-lg-6">
                        <div id="navbarRegion" >
                            <p>navbarRegion</p>
                        </div>
                    </div>
                </div>
            </div>
        </div>        
        
        <div class="row">
            <div class="col-lg-12">
                <h1>Hello Home Controller</h1>    
            </div>
        </div>        

        <script type="text/javascript">
            var marionetteApp = new MarionetteApp();
            marionetteApp.start();
        </script>
    </body>

Note that we are starting to use some bootstrap styles here – creating a navbar, containers and rows.

Unfortunately, running the app now will produce a dark navbar, with the navbar text very difficult to read, as well as overlapping our Hello Home Controller Text:

image

To fix this, create an app.css file in the /Content directory, with the following styles:

body { 
   padding-top : 60px; 
 } 

.app-navbar-text { 
   Font-family : Verdana,Arial,Sans-serif; 
   Font-size : 25px; 
   Font-style : Normal; 
   color : White; 
   padding-top : 5px; 
 } 

Now include this app.css in the Index.cshtml file:

    <link rel="stylesheet" href="../../Content/bootstrap.css" type="text/css" />
    <link rel="stylesheet" href="../../Content/app.css" type="text/css" />

And finally apply the app-navbar-text style to the divs in the navbar:

        <div class="navbar navbar-inverse navbar-fixed-top">
            <div class="container">
                <div class="row">
                    <div class="col-lg-6">
                        <div class="app-navbar-text">TypeScript Marionette</div>
                    </div>
                    <div class="col-lg-6">
                        <div id="navbarRegion" class="app-navbar-text">
                            <p>navbarRegion</p>
                        </div>
                    </div>
                </div>
            </div>
        </div>        

Your page should now at least be readable:

image

Referencing the Region in the Application

To reference an html div as a Region in Marionette, we simply need to call the Marionette.Application function addRegions().  As this function is at the Application level, the easiest way to do this in TypeScript is to use a class property to store the Region as follows (note that the alert is now commented ) :

class MarionetteApp extends Marionette.Application {
    navbarRegion: Marionette.Region;
    constructor() {
        super();
        this.on("initialize:after", this.initializeAfter);
        this.addRegions({ navbarRegion: "#navbarRegion" });
    }
    initializeAfter() {
        //alert("initializeAfter called");
    }
}

We will use this region as a content placeholder to render our Views a bit later on.

Creating a Marionette.View

In order to render something within the region, we will need to create a Marionette.ItemView.

In much the same way as we provided a <div> as the html template for a Marionette.Region, we provide a <script> block with a type of “text/template” to a Marionette.ItemView to use as an html template.  Modify your index.cshtml file to include the following:

        <script type="text/template" id="navBarItemViewTemplate">
            <p>NavBar View Template</p>
        </script>

To create a Marionette.ItemView in TypeScript, simply derive a class from Marionette.ItemView.  To do this, create a views directory under /tscode, and add a new TypeScript file named NavBarItemView.ts.

image

The code for NavBarItemView.ts is shown below.  Note that the reference paths at the top of this file will need to change slightly in order to correctly reference .d.ts files, as we are now two directories up from the base directory.

In the constructor we can see that the code is initializing the options parameter before it passes it to the base class.    To use the template that we created above, simply assign the template property of the options parameter to reference our <script> by id. :

/// <reference path="../../Scripts/typings/jquery/jquery.d.ts"/>
/// <reference path="../../Scripts/typings/underscore/underscore.d.ts"/>
/// <reference path="../../Scripts/typings/backbone/backbone.d.ts"/>
/// <reference path="../../Scripts/typings/marionette/marionette.d.ts"/> 

class NavBarItemView extends Marionette.ItemView {
    constructor(options?: any) {
        if (!options)
            options = {};
        options.template = "#navBarItemViewTemplate";
        super(options);
    }
}

Now modify your index.cshtml file to include the generated NavBarItemView.js file:

    <script language="javascript" type="text/javascript" src="../../tscode/MarionetteApp.js"></script>
    <script language="javascript" type="text/javascript" src="../../tscode/views/NavBarItemView.js"></script>
</head>

Lastly, modify the MarionetteApp to call show() on this view:

class MarionetteApp extends Marionette.Application {
    navbarRegion: Marionette.Region;
    constructor() {
        super();
        this.on("initialize:after", this.initializeAfter);
        this.addRegions({ navbarRegion: "#navbarRegion" });
    }
    initializeAfter() {
        //alert("initializeAfter called");
        this.navbarRegion.show(new NavBarItemView());
    }
}

If we run the application now ( hit F5 ) – we should see the “navBar region” text in the page replaced by the html we  specified in navBarItemViewTemplate :

image

Using bootstrap to create a clickable NavBar Button.

Our NavBarItemView is all well and good, but let’s do something useful with it. 

Most navigation bars are used to provide site-wide functionality – such as login / logout – so let’s update our NavBarItemView to show a boostrap button, and make it clickable.

To render our NavBarItemView as a bootstrap button, our template simply needs to have a css class assigned to it.  Modify the constructor of NavBarItemView to set the options.classname as follows:

class NavBarItemView extends Marionette.ItemView {
    constructor(options?: any) {
        if (!options)
            options = {};
        options.template = "#navBarItemViewTemplate";
        options.className = "btn btn-primary";
        super(options);
    }
}

Running the app now will assign the correct classnames to our template rendering it as a bootstrap button:

image

In order to make the button clickable, simply set the options.events property.  We will need to specify a function to call when the click event is fired.  This function can be called anything.  In the sample below, I’ve called it onClickEvent():

class NavItemBarView extends Marionette.ItemView {
    constructor(options?: any) {
        if (!options)
            options = {};
        options.template = "#navBarItemViewTemplate";
        options.className = "btn btn-primary";
        options.events = { "click": this.onClickEvent };
        super(options);
    }
    onClickEvent() {
        alert('NavBarItemView clicked');
    }
}

Run the app now, click on the navbar button, and verify that the event is fired correctly:

image

How simple was that ?

At this point it would be nice to render a couple of buttons in the navbar – but really this should be data-driven and not require too many changes to our ItemView.

Enter Backbone models and collections.

Let’s define a Backbone.Model to hold two properties for our buttons: Name and Id.  Then let’s create a collection of these models to drive rendering of multiple navbar buttons.

Using Backbone Models to drive NavBar buttons:

I’ve blogged previously about using strongly typed Backbone models with TypeScript. ( Can’t believe that was a year ago already ! ).   So if you would like a refresher then go an have a look.  Basically, we will be using ES5 syntax for model properties.  Go ahead and create a models directory under /tscode, and add a TypeScript file called NavBarButtonModel.ts.  Create a class that extends from Backbone.Model, called NavBarButtonModel.  Note that we also create a TypeScript interface definition for this model – which will help us when we start working with nested JSON and nested Backbone models and collections.

The constructor takes the INavBarButtonModel as input, and then sets each of the properties in the for loop.  This simple technique of using ES5 syntax and the constructor ensures that the model is synched with the underlying Backbone get and set functions, as well as giving us full type safety.

The source for NavBarButtonModel is as follows:

/// <reference path="../../Scripts/typings/jquery/jquery.d.ts"/>
/// <reference path="../../Scripts/typings/underscore/underscore.d.ts"/>
/// <reference path="../../Scripts/typings/backbone/backbone.d.ts"/>
/// <reference path="../../Scripts/typings/marionette/marionette.d.ts"/> 

interface INavBarButtonModel {
    Name?: string;
    Id?: number;
}

class NavBarButtonModel extends Backbone.Model implements INavBarButtonModel {
    get Name(): string { return this.get('Name'); }
    set Name(value: string) { this.set('Name', value); }

    get Id(): number { return this.get('Id'); }
    set Id(value: number) { this.set('Id', value); }

    constructor(input: INavBarButtonModel) {
        super();
        for (var key in input) {
            if (key) { this[key] = input[key]; }

        }
    }
}

Creating a Backbone.Collection to hold multiple Models.

Now that we have a Backbone.Model defined, lets define a Backbone.Collection to hold multiple buttons.  Create a NavBarButtonCollection.ts file in the models directory.  To define a Backbone.Collection, all we need to do is extend from Backbone.Collection, and set our model property to the name of the model class as follows.  Note that we have added a reference path at the top of the file to point to our model’s TypeScript file:

/// <reference path="../../Scripts/typings/jquery/jquery.d.ts"/>
/// <reference path="../../Scripts/typings/underscore/underscore.d.ts"/>
/// <reference path="../../Scripts/typings/backbone/backbone.d.ts"/>
/// <reference path="../../Scripts/typings/marionette/marionette.d.ts"/> 
/// <reference path="./NavBarButtonModel.ts" />

class NavBarButtonCollection extends Backbone.Collection {
    constructor(options?: any) {
        super(options);
        this.model = NavBarButtonModel;
    }
}

This collection will eventually be populated by JSON retrieved from a WebAPI service. But for now we can create a new collection as in the code below.  Note that this code is just a sample of how to create a collection – we will include it in the MarionetteApp.ts file a little later.:

        var navBarButtonCollection: NavBarButtonCollection = new NavBarButtonCollection(
            [
                { Name: "Home", Id: 1 },
                { Name: "About", Id: 2 },
                { Name: "Contact Us", Id: 3 }
            ]);

  But once we have a collection of NavBarButtonModels, we will need a new View to render the collection.

Creating a Marionette.CompositeView to render collections.

We now have most of the building blocks in place in order to render this collection on our page.  The final piece is a view that will take the NavBarButtonCollection as unput, and then instantiate a NavBarItemView for each model found in the collection.

Let’s create a new Marionette.CompositeView for this purpose.    As usual, we first need to create a <script> region in our Index.cshtml file – which will serve as the html template for our composite view.  Modify the Index.cshtml file to add a template with the id of navBarCollectionViewTemplate .  For the moment, we will simply define the template, but leave it blank as follows:

        <script type="text/template" id="navBarCollectionViewTemplate">
        </script>

Next, create a new TypeScript file under /tscode/views named NavBarCollectionView.ts.  This class will extend from Marionette.CompositeView.   As usual, specify the name of the html template in the options.template property as below:

Now we need to set the the itemView property to the class name of the view that is responsible for rendering an item – which in our case is NavBarItemView:

/// <reference path="../../Scripts/typings/jquery/jquery.d.ts"/>
/// <reference path="../../Scripts/typings/underscore/underscore.d.ts"/>
/// <reference path="../../Scripts/typings/backbone/backbone.d.ts"/>
/// <reference path="../../Scripts/typings/marionette/marionette.d.ts"/> 
/// <reference path="./NavBarItemView.ts"/> 

class NavBarCollectionView extends Marionette.CompositeView {
    constructor(options?: any) {
        if (!options)
            options = {};
        options.template = "#navBarCollectionViewTemplate";
        super(options);
        this.itemView = NavBarItemView;
    }
}

Next, we will need to include the new .js files in our Index.html <head> section:

    <script language="javascript" type="text/javascript" src="../../tscode/MarionetteApp.js"></script>

    <script language="javascript" type="text/javascript" src="../../tscode/views/NavBarItemView.js"></script>
    <script language="javascript" type="text/javascript" src="../../tscode/views/NavBarCollectionView.js"></script>
    
    <script language="javascript" type="text/javascript" src="../../tscode/models/NavBarButtonCollection.js"></script>
    <script language="javascript" type="text/javascript" src="../../tscode/models/NavBarButtonModel.js"></script>

Finally, we need to create an instance of the collection and pass it to the NavBarCollectionView.  Modify the MarionetteApp.ts file as shown below. 

Note that we create a NavBarButtonCollection by simply passing an array of objects – very similar to what raw JSON would look like.  Also, we create a NavBarCollectionView and pass it the collection in another JSON style object – by setting the collection property to the newly created NavBarButtonCollection:

class MarionetteApp extends Marionette.Application {
    navbarRegion: Marionette.Region;
    constructor() {
        super();
        this.on("initialize:after", this.initializeAfter);
        this.addRegions({ navbarRegion: "#navbarRegion" });
    }
    initializeAfter() {
        var navBarButtonCollection: NavBarButtonCollection = new NavBarButtonCollection(
            [
                { Name: "Home", Id: 1 },
                { Name: "About", Id: 2 },
                { Name: "Contact Us", Id: 3 }
            ]);

        this.navbarRegion.show(new NavBarCollectionView({ collection: navBarButtonCollection }));
    }
}

Firing up the app now should show us three buttons in our navbar – although it’s not quite what we envisaged.

image

Rendering Model Properties in templates.

What we really want here is to modify our ItemView template to display the Name value of the NavBarButtonModel instead of NavBarItem View Template.

Thankfully, it’s a piece of cake.

Modify the <script type="text/template" id="navBarItemViewTemplate"> tag in Index.cshtml as follows:

        <script type="text/template" id="navBarItemViewTemplate">
            <%= Name %>
        </script>

Running the app now will render the button names based on the model properties:

image

Finally, lets modify our click event in NavBarItemView to read the Id from the NavBarButtonModel:  You may notice that the call to get the Id property from the model is NOT using ES5 syntax.  As far as I understand, this is because Backbone is using the base Backbone.Model class internally, and therefore relies on the base get(‘attribute’) functions.

class NavBarItemView extends Marionette.ItemView {
    constructor(options?: any) {
        if (!options)
            options = {};
        options.template = "#navBarItemViewTemplate";
        options.className = "btn btn-primary";
        options.events = { "click": "onClickEvent" };
        super(options);
    }
    onClickEvent() {
        alert('NavBarItemView clicked with id :' + this.model.get('Id'));
    }
}

Clicking on any one of the buttons will now show a message with the model’s Id.

image

Triggering a Marionette event.

For the final exercise of Part 1, let’s use Marionette events to notify the MarionetteApp when someone clicks on a Navbar menu item.

Modify the onClickEvent() of the NavBarItemView to call Marionette’s trigger() function.  When triggering an event, we will need an event name (which can be anything), and we can also attach any data we need to the event.  In the code below, we are attaching the Model’s Id to the event.

class NavBarItemView extends Marionette.ItemView {
    constructor(options?: any) {
        if (!options)
            options = {};
        options.template = "#navBarItemViewTemplate";
        options.className = "btn btn-primary";
        options.events = { "click": "onClickEvent" };
        super(options);
    }
    onClickEvent() {
        this.trigger("navbar:clicked", this.model.get('Id'));
    }
}

Listening to a Marionette.Event

To handle this even, we will make some changes to the MarionetteApp.  Basically, just call the

on(‘eventname’,callback) method on the view, and provide a function callback (this.navBarButtonClicked).  Note too that the listening eventName is slightly different to the trigger eventName: itemview:navbar:clicked (handler) as opposed to just navbar:clicked ( trigger ).  This is because Marionette automatically attaches itemview: to any event that is fired by an ItemView.

Also note the signature of the callback method: we have two parameters – itemView and buttonId.  Again, Marionette always sends a handle to the originating ItemView as the first parameter to an event handler.  The second parameter therefore is our Model’s Id.

class MarionetteApp extends Marionette.Application {
    navbarRegion: Marionette.Region;
    constructor() {
        super();
        this.on("initialize:after", this.initializeAfter);
        this.addRegions({ navbarRegion: "#navbarRegion" });
    }
    initializeAfter() {
        var navBarButtonCollection: NavBarButtonCollection = new NavBarButtonCollection(
            [
                { Name: "Home", Id: 1 },
                { Name: "About", Id: 2 },
                { Name: "Contact Us", Id: 3 }
            ]);

        var navBarView = new NavBarCollectionView({ collection: navBarButtonCollection });

        navBarView.on("itemview:navbar:clicked", this.navBarButtonClicked);

        this.navbarRegion.show(navBarView);
    }

    navBarButtonClicked(itemView: Marionette.ItemView, buttonId: number) {
        alert('Marionette.App handled NavBarItemView clicked with id :' + buttonId);
    }

}

Running our App now – and clicking on a NavBarButton will then fire a Marionette event, which is then handled by the Marionette.App itself:

image

Well, that’s it for Part 1 of this article.

As mentioned, Part 2 of this article we will cover the following:

  • Creating an ASP.NET WebAPI Data Controller.
  • Unit testing the DataController in C#
  • Updating the NavBarButtonCollection to use our DataController.
  • Unit testing the NavBarButtonCollection using Jasmine.
  • Creating a Marionette.CompositeView to render data in a bootstrap table.

Have fun,

blorkfish.

22 Responses to TypeScript : Using Backbone.Marionette and REST WebAPI (Part 1)

  1. Alastair says:

    Hi, great article, thanks! Noticed a typo in your first ‘App.ts’ code – you’ve got:

    this.on(“initialise:after”, this.initialiseAfter);

    …when it should be

    this.on(“initialize:after”, this.initialiseAfter);

    (and to be consistent, the callback function should probably also be initializeAfter as well).

    It looks like you’ve fixed it in github.

    cheers,
    Alastair

  2. Alastair says:

    Hi, another wee thing: when you first create the NavBarItemView.ts and then try to use it in your MarionetteApp, I think you also need to add this to the App.ts file:

    ///

    …otherwise the typescript compiler can’t find it. Is that right?

    cheers,
    Alastair

  3. Alastair says:

    Oops, the comment editor swallowed my code. I meant that you needed:

    reference path=”views/NavBarItemView.ts”

    cheers,
    Alastair

  4. Alastair says:

    Hi, another one!

    You create the NavBarItemView class, but then in the next section “Using bootstrap to create a clickable NavBar Button” you refer to it as the NabBarView class, in the text and the code. Later on you refer to it again as NavBarItemView.

    I think in all cases this should be NavBarItemView?

    cheers,
    Alastair

    • blorkfish says:

      Hey Alastair,
      Many many thanks for pointing these typos out. I’ll make some updates tonight.
      Thanks again for taking the time to comment. I’ve been over the article and the code so many times, but still seemed to have missed a few.
      Have fun, blorkfish.

      • Alastair says:

        No problem – it was a very helpful tutorial!

        The nice thing about typescript is that these typos caused real compiler problems that I could track down 🙂

        Couple more things:

        1. At first you refer to ‘App.ts’, but later you refer to ‘MarionetteApp.ts’

        2. Not a typo really, but in NavBarItemView you have:

        options.events = { “click”: “onClickEvent” };

        …when in fact you can use:

        options.events = { “click”: this.onClickEvent };

        The latter option gives you typescript compiler checking for free. You already use the same technique for the “initialize:after” event in MarionetteApp.

        cheers,
        Alastair

  5. Andreas says:

    Great post, many thanks – it helped me a lot. But I may have found a bug – you wrote:

    Finally, lets modify our click event in NavBarItemView to read the Id from the NavBarButtonModel: You may notice that the call to get the Id property from the model is NOT using ES5 syntax. As far as I understand, this is because Backbone is using the base Backbone.Model class internally, and therefore relies on the base get(‘attribute’) functions

    The reason for this is probably the constructor of NavBarButtonCollection. Your code says:

    options.template = “#navBarCollectionViewTemplate”;
    super(options);
    this.itemView = NavBarItemView;

    but I think it schould read:

    options.template = “#navBarCollectionViewTemplate”;
    this.itemView = NavBarItemView;
    super(options);

    With this little modification the ES5 syntax works (because the right model is constructed).

    Regards,
    Andreas

  6. Maurizio says:

    Thank you for this article.

    When I extend Marionette.Application class I have this error:
    Generic type references must include all type arguments.

    No one has this error?

  7. DavidW says:

    When I add the collection I get the following error on the this.model = NavBarButtonModel line:
    Error 3 Cannot convert ‘typeof NavBarButtonModel’ to ‘new() => any’:
    Construct signatures of types ‘typeof NavBarButtonModel’ and ‘new() => any’ are incompatible:
    Call signature expects 0 or fewer parameters. c:\Users\Me\documents\visual studio 2013\Projects\RGArt\RGArt\tscode\models\NavBarButtonCollection.ts 10 9 RGArt

    • DavidW says:

      The code:
      class NavBarButtonCollection extends Backbone.Collection {
      constructor(options?: any) {
      super(options);
      this.model = NavBarButtonModel;
      }
      }

  8. DavidW says:

    I have an ‘any’ in angle brackets after extends Backbone.Collection and another reader implied it seems to be necessary to get things to compile

    • blorkfish says:

      Hey DavidW,
      This seems to be caused by a new version of the backbone.d.ts type definition file. The version included in the github repository for this project will compile correctly. I’ll need to do some investigation on this one, and update the article accordingly.
      Please let us know if the extends Backbone.Collection syntax works for you.
      – Have fun – blorkfish.

  9. milkshakeuk says:

    Hi I always get the following message:

    …src\js\app\app.ts(6,21): error TS2173: Generic type references must include all type arguments.

    Process finished with exit code 1

    which referres to: class MyApp extends Marionette.Application

    is there any reason for this?

    • blorkfish says:

      Hey milkshakeuk,
      See my previous comments to DavidW.
      It seems that the definitelyTyped versions of Backbone and Marionette are now using generics for type definitions.
      This article is using an older version of the .d.ts files – one that does NOT use generics.
      Where we used to write MyApp extends Marionette.Application, we now need to write MyApp extends Marionette.Application |any| {}.
      I have not worked through this new generic style syntax, but do hope to update the article when I can.
      If you download the source code from this article, it has the older versions of the .d.ts files included.
      Have fun, -blorkfish.

  10. milkshakeuk says:

    Thank you for the reply I will try your suggestion when I get chance, and report back.
    Thanks for the article btw, I have been using backbone marionette at work and wanted to have a go at TypeScript so this article is very much appreciated.

  11. milkshakeuk says:

    the following code does not lead to an alert on after initialize:

    /// <reference path="../../../typings/jquery/jquery.d.ts"/>
    /// <reference path="../../../typings/underscore/underscore.d.ts"/>
    /// <reference path="../../../typings/backbone/backbone.d.ts"/>
    /// <reference path="../../../typings/marionette/marionette.d.ts"/>
    
    class MyApp extends Marionette.Application<any> {
        constructor() {
            super();
            this.on("initialize:after", this.initializeAfter);
        }
        initializeAfter() {
            alert("initializeAfter called");
        }
    }
    
  12. milkshakeuk says:

    has comments stopped working?

  13. milkshakeuk says:
    function test(){};

Leave a Reply

Fill in your details below or click an icon to log in:

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

%d bloggers like this: