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

This is Part 2 of an article that aims to walk the reader through setting up a Backbone.Marionette SPA application with Visual Studio 2013, and in particular, write Marionette apps using TypeScript.

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

Part 1 can be found here, and covered the 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.
    In this part of the 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

      So let’s get started.

      Creating an ASP.NET WebAPI Data Controller.

      Creating an ASP.NET WebAPI Data Controller is just as simple as creating a normal MVC Controller.  All we need to do is to derive our class from ApiController instead of Controller, and then specify what the url will be when calling this controller. 

      The latter part is is accomplished by adding two attributes to a method call on our ApiController. These are the [Route] attribute – to specify the url, and a System.Web.Http attribute to specify whether this is a REST get, post or delete.
      Go ahead and create a file under the /Controllers directory named HomeDataController.
      Derive this class from ApiController ( in the System.Web.Http namespace ), and add a Route attribute, as well as an HttpGet attribute as in the code below :
      using System.Collections.Generic;
      using System.Net;
      using System.Net.Http;
      using System.Web.Http;
      
      namespace typescript_marionette.Controllers
      {
          public class HomeDataController : ApiController
          {
              [Route("api/dataservices")]
              [HttpGet]
              public HttpResponseMessage GetDataTable()
              {
                  return Request.CreateResponse<IEnumerable<string>>(HttpStatusCode.OK, GetData());
              }
      
              public List<string> GetData()
              {
                  return new List<string> {"test1", "test2"};
              }
      
          }
      }
      There are a couple of things to note about the code above.
      Firstly, the [Route(“api/dataservices”)] attribute.  This defines the route to our DataController function.  So firing up a web-browser and pointing it to /api/dataservices will hit this DataController. 
      Secondly the [HttpGet] attribute.  This attribute defines the method signature as allowing REST GETs.
      Thirdly, the return type of HttpResponseMessage – and the return syntax : return Request.CreateResponse <type>.  These two signatures will return data JSON when json is requested, or XML when xml is requested.
      Fourth, the HttpStatusCode.OK will return a success callback to any JavaScript calling code.  Interestingly enough, throwing an Exception anywhere in the call stack will return an error callback to any JavaScript calling code.  This is all built-in when using classes deriving from ApiController.  Later on, we will create a Jasmine unit test to test our error callback – to make sure that we are handling errors correctly.
      Next, note that I have created a method public List<string> GetData(), where I could have simply created this List<string> within the GetDataTable() method directly.  Splitting these methods will help us with unit-testing later on in the piece.  One C# xUnit test will target the GetData() function, and then we will use Jasmine to unit-test the returned Json response in GetDataTable().
      Lastly, the IEnumerable<type> syntax.  In the code sample above, we are simply returning a string type.  But further down the line, we will define [Serializable] POCOs to return nested Json, such that returning IEnumerable<MyType> will return full MyType classes – as well as any child classes or collections defined – automatically transformed into Json or Xml.  Cool, huh ?
      Unfortunately, firing up web-browser, and typing in the url /api/dataservices at this stage will not work.  Using IE, you will get the very helpful error message “The webpage cannot be found”, with a 404 status:

    image

    Using Chrome, the error message is slightly more helpful: No HTTP resource was found that matches the request URI ‘http://localhost:65147/api/dataservices&#8217;.

    This is down to one missing line of code.  Navigate to the App_Start directory, and double-click on the WebApiConfig.cs file.  Modify the code to call config.MapHttpAttributeRoutes() as shown below.  This line of code is called on app startup, and simply traverses the code to find our [Http] and [Route] attributes – and then adds them to the Route Table.

    namespace typescript_marionette
    {
        public static class WebApiConfig
        {
            public static void Register(HttpConfiguration config)
            {
                config.MapHttpAttributeRoutes();
    
                config.Routes.MapHttpRoute(
                    name: "DefaultApi",
                    routeTemplate: "api/{controller}/{id}",
                    defaults: new { id = RouteParameter.Optional }
                );
            }
        }
    }
    

    Firing up Chrome at this stage, and navigating to /api/dataservices will now generate the expected result : a string array with two entries : test1 and test2 :

    image

    I have always found that Chrome or Firefox is far easier to work with when manually testing DataControllers.  IE for some reason does not have a default rendering engine for pure data.  It always tries to download the json response – and then you need to select a program to view this data – which is terribly annoying. 

    image

    image

    So stick to any other browser except IE when manually testing DataControllers.

    Unit-testing the WebAPI Data Controller in C#

    Right, so what would a good Data Controller be without unit tests ? 

    As far as I understand, Hamlet D’Arcy is credited with the saying “[Without unit tests] You’re not refactoring, you’re just changing shit.”

    So best we create some unit tests, before we start changing shit…

    Personally, I have been using xUnit as a testing framework for some time now. 

    Add a new project to your solution called typescript-marionette-xunit.  Make sure the project type is a Class Library:

    image

    Delete the Class1.cs file that is automagically created for you.

    Now let’s add the nuGet packages for xUnit.  To to Tools | Library Package Manager | Package Manager Console.

    At the top of the screen, next to the Package source dropdown, there is a Default project dropdown.  Make sure that you have selected the typescript-marionette-xunit project here :

    skitch_screenshot_1

    Now install xunit as follows:

    Install-Package xunit –Version 1.9.2

    Install-Package xunit.extensions –Version 1.9.2

    Next, add a reference to typescript-marionette project: Right-click on Refrences, Add Reference – then choose the typescript-marionette project under Solution Projects:

    image

    Now add a Controllers directory, and then a HomeDataControllerTests.cs class as follows:

    namespace typescript_marionette_xunit.Controllers
    {
        public class HomeDataControllerTests
        {
            [Fact]
            public void GetData_Returns_ListOfStrings()
            {
                HomeDataController controller = new HomeDataController();
                Assert.Equal(new List<string> {"test", "test"}, controller.GetData());
            }
        }
    }

    Running this unit-test will produce the following error:

    Assert.Equal() FailurePosition:

    First difference is at position 0

    Expected: List<String> { "test", "test" }

    Actual: List<String> { "test1", "test2" }

    I firmly believe that you should always write a test that fails first, before modifying your code to make the test pass.  Obviously this is as simple as changing the expected List<string> to be { “test1”, “test2” }.

    Modifying the WebAPI Data Controller to return a collection of nested C# POCO objects.

    It’s time now to get the DataController to return some real data.  For the purposes of this article, lets assume we are wanting to return a list of users, and how they scored per round.  The classes involved are as per the following class diagram:

    skitch_screenshot_2

    Create a ResultsModels.cs file under the /Models directory, as follows:

    namespace typescript_marionette.Models
    {
        [Serializable]
        public class UserModel
        {
            public UserModel()
            {
                RoundScores = new List<RoundScore>();
            }
            public string UserName;
            public string RealName;
            public List<RoundScore> RoundScores;
        }
    
        [Serializable]
        public class RoundScore
        {
            public int RoundNumber;
            public int TotalPoints;
        }
    }

    Now, let’s modify the HomeDataController to return a list of these models.  At the same time, we may as well define a new url (api/results) to return these results:

            [Route("api/results")]
            [HttpGet]
            public HttpResponseMessage GetUserResults()
            {
                return Request.CreateResponse<IEnumerable<UserModel>>
                    (HttpStatusCode.OK, GetUserResultModels());
            }
    
            public List<UserModel> GetUserResultModels()
            {
                return new List<UserModel>
                {
                    new UserModel { UserName = "testUser_1", RealName = "Test User No 1",
                        RoundScores =  new List<RoundScore>
                    {
                          new RoundScore { RoundNumber = 1, TotalPoints = 2 }
                        , new RoundScore { RoundNumber = 2, TotalPoints = 3 }
                        , new RoundScore { RoundNumber = 3, TotalPoints = 2 }
                        , new RoundScore { RoundNumber = 4, TotalPoints = 5 }
                    } },
                    new UserModel { UserName = "testUser_2", RealName = "Test User No 2", 
                        RoundScores =  new List<RoundScore>
                    {
                          new RoundScore { RoundNumber = 1, TotalPoints = 5 }
                        , new RoundScore { RoundNumber = 2, TotalPoints = 6 }
                        , new RoundScore { RoundNumber = 3, TotalPoints = 2 }
                        , new RoundScore { RoundNumber = 4, TotalPoints = 1 }
                    }  },
                    new UserModel { UserName = "testUser_3", RealName = "Test User No 3", 
                        RoundScores =  new List<RoundScore>
                    {
                          new RoundScore { RoundNumber = 1, TotalPoints = 3 }
                        , new RoundScore { RoundNumber = 2, TotalPoints = 5 }
                        , new RoundScore { RoundNumber = 3, TotalPoints = 6 }
                        , new RoundScore { RoundNumber = 4, TotalPoints = 6 }
                    }  }
                };
            }
    

    Now let’s fire up our application, and browse to /api/results ( using Chrome ) to see what we get:

    image

    Defining TypeScript Backbone.Model classes to match our nested class structure.

    At this point, we will need some Backbone.Model classes and a Backbone.Collection to retrieve data from our /api/results url.  Backbone.Collections have a very simple method of retrieving data from REST services – simply specify the url property.  As an example, if we were to modify the NavBarButtonCollection (that we created in Part 1) to load data from REST services, we would do the following:

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

    So let’s create some Backbone.Models to emulate the C# POCO class structure for UserModel and RoundScore that we built in C#.  The trick here is to use TypeScript interfaces to define the relationships.  In the /tscode/models directory, create a new TypeScript file named UserResultModels.ts.   For simplicity, I have included the interfaces and Models in the same TypeScript file.  The interface definitions for the returned Json objects are shown below.  Note how we are defining the nested properties as arrays [ ]. Also, the property names must match exactly the C# POCO property names.

    interface IRoundScore {
        RoundNumber?: number;
        TotalPoints?: number;
    }
    
    interface IUserModel {
        UserName?: string;
        RealName?: string;
        RoundScores?: IRoundScore [];
    }

    Next, we create the Backbone.Model class based on these interfaces.  Note that the ES5 property getters and setters match the signatures of the interfaces.

    class RoundScore extends Backbone.Model implements IRoundScore {
        get RoundNumber(): number { return this.get('RoundNumber'); }
        set RoundNumber(value: number) { this.set('RoundNumber', value); }
    
        get TotalPoints(): number { return this.get('TotalPoints'); }
        set TotalPoints(value: number) { this.set('TotalPoints', value); }
    
        constructor(input: IRoundScore) {
            super();
            for (var key in input) {
                if (key) { this[key] = input[key]; }
    
            }
        }
    }
    
    class UserModel extends Backbone.Model implements IUserModel {
        get UserName(): string { return this.get('UserName'); }
        set UserName(value: string) { this.set('UserName', value); }
    
        get RealName(): string { return this.get('RealName'); }
        set RealName(value: string) { this.set('RealName', value); }
    
        get RoundScores(): IRoundScore[] { return this.get('RoundScores'); }
        set RoundScores(value: IRoundScore[]) { this.set('RoundScores', value); }
    
        constructor(input: IRoundScore) {
            super();
            for (var key in input) {
                if (key) { this[key] = input[key]; }
    
            }
        }
    }

    Now to create our collection: ( again in /tscode/models/UserResultModels.ts )

    class UserResultCollection extends Backbone.Collection {
        constructor(options?: any) {
            super(options);
            this.model = UserModel;
            this.url = "/api/results";
        }
    }

    As a quick test of this collection, lets load it in our MarionetteApp as a variable.  Note that we have specified a property to the fetch({ async: false }) function of the UserResultCollection .  This property will halt execution of the calling thread (not asynchronous) until the collection is loaded.  In general, it is better practise NOT to specify this parameter, unless absolutely neccesary.  the initializeAfter() function in MarionetteApp.ts is shown below:

        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);
    
            var resultsCollection = new UserResultCollection();
            resultsCollection.fetch({ async: false });
        }

    Before trying to debug this code, don’t forget to include the UserResultModel.js file in our Views/Home/Index.cshtml file:

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

    Setting a breakpoint after the collection is loaded, and checking the resulting resultsCollection variable in Visual Studio should show that we have successfully loaded the json returned from our REST ApiController:

    skitch_screenshot_3

    But debugging and manually verifying that our collection is loaded correctly is just what it is : manual.  And manual is time-consuming, error-prone and just a pure pain.  So let’s write a unit-test to verify that our model is loading correctly from the C# DataController.

    Unit testing the Backbone Collection with jasmine

    To setup a unit test for our UserResultCollection, we will create a web-page named SpecRunner.html.  This is a simple web-page that just includes all of our required .js files, and then calls jasmine.execute(). 

    Firstly, create a /tscode/test directory – then add an html page to this directory named SpecRunner.html.  This file is very similar to /Views/Home/Index.cshtml, and should also include the meta tag http-equiv for IE.  Simply copy the <head> section from Index.cshtml.  As well as including all of our source .js files, we will also need to include /scripts/jasmine.js, , /scripts/jasmine-html.js, and also the /css/jasmine.css file as follows:

    <!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" />
        <link rel="stylesheet" href="../../Content/app.css" type="text/css" />
        
        <script language="javascript" type="text/javascript" src="../../Scripts/jasmine.js"></script>
        <script language="javascript" type="text/javascript" src="../../Scripts/jasmine-html.js"></script>
        <script language="javascript" type="text/javascript" src="../../Scripts/jasmine-jquery.js"></script>
    
        <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>
    
        <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>
    
        <script language="javascript" type="text/javascript" src="../../tscode/models/UserResultModels.js"></script>
    </head>
    <body>
        <script type="text/javascript">
            var jasmineEnv = jasmine.getEnv();
            jasmineEnv.addReporter(new jasmine.HtmlReporter());
            jasmineEnv.execute();
        </script>
    </body>
    </html>

    Note that the jasmine-jquery.js file is not included via the nuGet package for jasmine-js.  You will need to download the file from here [ jasmine-jquery-1.3.1.js  ] – and then save it into your /Scripts directory.

    To run this file, simply right-click on it, and select the menu option Set As Start Page, and then hit F5 to debug.  But don’t do it yet – if you do – you will end up with a blank page.  Why ? Well, we havn’t written any jasmine tests yet.

    Writing Jasmine unit tests for our Backbone Collection.

    In the /tscode/test directory, create a directory named models.  Now create a TypeScript file for our UserResultCollection tests named UserResultCollectionTests.ts. , and include the reference paths for our definition files at the top.

    Jasmine tests all fall within a describe(‘ test suite name ’, () => { .. tests go here … }) block – which defines the test suite name.  Within this describe function, each test is defined with the syntax it(‘ test description ‘ , () => {  test goes here… }) function 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"/> 
    /// <reference path="../../../Scripts/typings/jasmine/jasmine.d.ts"/> 
    /// <reference path="../../../Scripts/typings/jasmine-jquery/jasmine-jquery.d.ts"/> 
    
    describe('/tscode/test/models/UserResultCollectionTests.ts ', () => {
    
        it('should fail', () => {
            expect('undefined').toBe('defined');
        });
    
    });

    Now, just include the generated .js file in your SpecRunner.html file:

        <script language="javascript" type="text/javascript" src="../../tscode/models/NavBarButtonCollection.js"></script>
        <script language="javascript" type="text/javascript" src="../../tscode/models/NavBarButtonModel.js"></script>
    
        <script language="javascript" type="text/javascript" src="../../tscode/models/UserResultModels.js"></script>
        <script language="javascript" type="text/javascript" src="./models/UserResultCollectionTests.js"></script>

    Running the app now ( F5 ) should show the results of the jasmine tests:

    image

    Ok, now that we have Jasmine up and running, lets write some unit tests for our UserResultCollection.  Jasmine has two functions – beforeEach() and afterEach() that will run before each test, and after each test to perform initialization.  In beforeEach(), we setup our collection, and then we can re-use it in each of our tests as follows:

    describe('/tscode/test/models/UserResultCollectionTests.ts ', () => {
    
        var userResultCollection: UserResultCollection;
    
        beforeEach(() => {
            userResultCollection = new UserResultCollection();
            userResultCollection.fetch({ async: false });
        });
    
        it('should return 3 records from HomeDataController', () => {
            expect(userResultCollection.length).toBe(3);
        });
    
    });

    To find a specific instance in this collection, we can use the underscore.js functions where() and findWhere()where() will return a collection where all elements match the criteria, and findWhere() will return a single Model in our collection that matches the selection criteria:

        it('should find 1 UserModel with Name testUser_1', () => {
            var userModels = userResultCollection.where({ UserName: 'testUser_1' });
            expect(userModels.length).toBe(1);
    
        });
    
        it('should return a UserModel with Name testUser_1', () => {
            var userModel = userResultCollection.findWhere({ UserName: 'testUser_1' });
            expect(userModel).toBeDefined();
        });

    Jasmine tests can also be nested.  This means that we can describe( ) a set of tests that will use the parent’s beforeEach() and afterEach() functions to run the tests.   In this describe() block, we can also create beforeEach() functions.  This provides us with a handy way of testing a single model within the userResultCollection:

        it('should return a UserModel with Name testUser_1', () => {
            var userModel = userResultCollection.findWhere({ UserName: 'testUser_1' });
            expect(userModel).toBeDefined();
        });
    
        // this describe block is nested inside our main describe block
        describe(' UserModel tests ', () => {
            var userModel: UserModel;
            beforeEach(() => {
                // the userResultCollection is setup in the parent beforeEach() function
                userModel = <UserModel> userResultCollection.findWhere({ UserName: 'testUser_1' });
            });
    
            it('should set UserName property', () => {
                expect(userModel.UserName).toBe('testUser_1');
            });
    
            // check that we are getting an array for our nested JSON objects
            it('should set RoundScores property', () => {
                expect(userModel.RoundScores.length).toBe(4);
            });
        });

    Now lets use the same technique to get the third RoundScore model from the array of RoundScores for this UserModel;

            // check that we are getting an array for our nested JSON objects
            it('should set RoundScores property', () => {
                expect(userModel.RoundScores.length).toBe(4);
            });
    
            // nested describe block re-uses the userModel set in parent beforeEach()
            describe('RoundScore tests', () => {
                var roundScore: RoundScore;
                beforeEach(() => {
                    roundScore = <RoundScore> userModel.RoundScores[2]; // get the third RoundScore
                });
    
                it('should have RoundNumber set to 3', () => {
                    expect(roundScore.RoundNumber).toBe(3);
                });
                it('should have TotalPoints set to 2', () => {
                    expect(roundScore.TotalPoints).toBe(2);
                });
            });

    image

    Creating a Marionette.CompositeView to render the Backbone Collection in a table.

    So we are now confident that our Backbone Collection is working correctly.  Next step is to create a Marionette.CompositeView to render this collection in a table.  In the /tscode/views directory, create a new TypeScript file named UserResultViews.ts.  Once again, extend from Marionette.CompositeView, and set the options.template property:

    class UserResultsView extends Marionette.CompositeView {
        constructor(options?: any) {
            if (!options)
                options = {};
            options.template = "#userResultsViewTemplate";
            super(options);
        }
    }

    Next, update the Index.cshtml to provide an html snippet that matches the options.template property (#userResultsViewTemplate) above.  While we are at it, lets also create a new Marionette Region for our UserResultView to render into.  Modify the Index.cshtml as follows.  Don’t forget to include the new JavaScript file in the <head> element.

        <script language="javascript" type="text/javascript" src="../../tscode/views/UserResultViews.js"></script>
            <div class="container"> @* wrap the row with a container *@ 
                <div class="row">
                    <div class="col-lg-12">
                        @*<h1>Hello Home Controller</h1> // old code *@
                        <div id="userResultRegion"></div> @*  new region  *@
                    </div>
                </div>
            </div>
            
            @*  new template  *@ 
            <script type="text/template" id="userResultsViewTemplate">
                This is the userResultsViewTemplate.
            </script>

    Next, we create need to modify our MarionetteApp to include the new region, create a UserResultView, and show this view in the region:

    class MarionetteApp extends Marionette.Application {
        navbarRegion: Marionette.Region;
        userResultRegion: Marionette.Region; // new region
        constructor() {
            super();
            this.on("initialize:after", this.initializeAfter);
            this.addRegions({ navbarRegion: "#navbarRegion" });
            this.addRegions({ userResultRegion: "#userResultRegion" }); // new region
        }
        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);
    
            var userResultView = new UserResultsView(); // create the new view
            this.userResultRegion.show(userResultView); // show the view
        }
        navBarButtonClicked(itemView: Marionette.ItemView, buttonId: number) {
            alert('Marionette.App handled NavBarItemView clicked with id :' + buttonId);
        }
    }

    If all goes well, we should see the new template displayed on the page:

    image

    Using a Marionette.CompositeView as an ItemView:

    Now that we have the top-level view rendering correctly, lets create another CompositeView to serve as the view for each user in our UserResultCollection.  Simply create another CompositeView named UserResultItemView, give it a template, and then set the parent itemView property to the new class name as follows:

    class UserResultsView extends Marionette.CompositeView {
        constructor(options?: any) {
            if (!options)
                options = {};
            options.template = "#userResultsViewTemplate";
            super(options);
            this.itemView = UserResultItemView; // set the child view here
        }
    }
    
    // new ItemView class
    class UserResultItemView extends Marionette.CompositeView {
        constructor(options?: any) {
            if (!options)
                options = {};
            options.template = "#userResultItemViewTemplate"; // new template
            super(options);
        }
    }

    Next, create the html for userResultItemViewTemplate in the Index.cshtml:

            <script type="text/template" id="userResultItemViewTemplate">
                This is the userResultItemViewTemplate for : <%= UserName %>
            </script>

    Finally, construct and fetch a new UserResultCollection in the MarionetteApp, and pass this collection to the UserResultsView:

            var userResultCollection = new UserResultCollection();
            userResultCollection.fetch({ async: false });
    
            var userResultView = new UserResultsView({ collection: userResultCollection }); // pass in the collection
            this.userResultRegion.show(userResultView);

    Running our app now will render an item for each element found in our UserResultCollection:

    image

    Rendering nested Backbone.Collections.

    Cool.  So now we need another ItemView to render our RoundScores per user ( this is the nested collection within our Users collection.. All we need is a ResultItemView to render a single RoundScore, then set the parent itemView property to our new child view, exactly as we did before.  Remember to create an html template in our Index.cshtml a well:

    class UserResultItemView extends Marionette.CompositeView {
        constructor(options?: any) {
            if (!options)
                options = {};
            options.template = "#userResultItemViewTemplate"; 
            super(options);
            this.itemView = ResultItemView; // set the child view here
        }
    }
    
    // new ResultItemView class
    class ResultItemView extends Marionette.ItemView {
        constructor(options?: any) {
            if (!options)
                options = {};
            options.template = "#resultItemViewTemplate"; // new template
            super(options);
        }
    }

    Index.cshtml:

            <script type="text/template" id="resultItemViewTemplate">
                This is the resultItemViewTemplate
            </script>

    Running our app now should show three ResultItemView s per user, right ?

    image

    Ok, so what went wrong ?

    In order to use an ItemView, the composite view needs it’s collection property set correctly.  Remember that when we instantiated the top level view, we passed our collection in the constructor:

    var userResultView = new UserResultsView({ collection: userResultCollection }); // pass in the collection

    Each item in this collection creates a new instance of the UserResultItemView class, and passes it the model to render.  So all we need to do is to set our collection property in the constructor, and create our own internal collection based on the incoming model.  Before we do this, however, lets just create a quick collection to hold RoundScores.  In /models/UserResultModels, create a new collection named RoundScoreCollection to hold RoundScore models as follows:

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

    Now we can modify the constructor of UserResultItemView to set the collection

    class UserResultItemView extends Marionette.CompositeView {
        constructor(options?: any) {
            if (!options)
                options = {};
            options.template = "#userResultItemViewTemplate"; 
            super(options);
            this.itemView = ResultItemView; 
            // set internal collection:
            this.collection = new RoundScoreCollection(options.model.RoundScores);
        }
    }

    Running the app now will render correctly:

    image

    Using CompositeView properties to generate html

    One of the advantages of using Marionette.Composite views is the ability to control the rendered html.  Let’s update our template html and Views to render results in a table. 

    Firstly, modify the html template for userResultsViewTemplate to create a <thead> and <tbody> as follows:

            <script type="text/template" id="userResultsViewTemplate">
                <thead>
                    <tr>
                        <th>UserName</th>
                        <th>1</th>
                        <th>2</th>
                        <th>3</th>
                        <th>4</th>
                    </tr>
                </thead>
                <tbody></tbody>
            </script>

    Obviously, we need to wrap this html in a topmost <table> tag – and render our child views within the <tbody> html region.  These two settings are made in the UserResultsView:

    class UserResultsView extends Marionette.CompositeView {
        constructor(options?: any) {
            if (!options)
                options = {};
            options.template = "#userResultsViewTemplate";
            options.tagName = "table"; // outer tag
            options.itemViewContainer = "tbody"; // itemview container
            super(options);
            this.itemView = UserResultItemView; 
        }
    }

    Now update the html template for userResultItemViewTemplate to wrap the UserName property in a <td> tag, and set the outer tagName for the UserResultItemView to <tr>:

            <script type="text/template" id="userResultItemViewTemplate">
                <td><%= UserName %></td>
            </script>
    class UserResultItemView extends Marionette.CompositeView {
        constructor(options?: any) {
            if (!options)
                options = {};
            options.template = "#userResultItemViewTemplate";
            options.tagName = "tr"; // outer tagname
            super(options);
            this.itemView = ResultItemView; 
            this.collection = new RoundScoreCollection(options.model.RoundScores);
        }
    }

    Finally, update the html resultItemViewTemplate to render TotalPoints in a div, and update the tagName to use <td>:

            <script type="text/template" id="resultItemViewTemplate">
                <div><%= TotalPoints %></div>
            </script>
    class ResultItemView extends Marionette.ItemView {
        constructor(options?: any) {
            if (!options)
                options = {};
            options.template = "#resultItemViewTemplate";
            options.tagName = "td"; // outer tagname
            super(options);
        }
    }

    Running our app now will create a table as follows:

    image

    Using bootstrap styles in a Marionette.CompositeView

    Finally, lets add some classes to our CompositeView to use bootstrap styles to render the table.  Update the UserResultsView and specify the className property:

    class UserResultsView extends Marionette.CompositeView {
        constructor(options?: any) {
            if (!options)
                options = {};
            options.template = "#userResultsViewTemplate";
            options.tagName = "table";
            options.className = "table table-hover"; // inject a class 
            options.itemViewContainer = "tbody"; 
            super(options);
            this.itemView = UserResultItemView; 
        }
    }

    Running the app now gives us our final result.  Rendered html based on nested Json from a WebAPI DataController:

    image

    And that wraps it up.

    Have fun,

    blorkfish.

    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.