TypeScript : Using Backbone.Marionette and REST WebAPI (Part 1)
March 22, 2014 23 Comments
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:
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:
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.
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.
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:
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:
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:
Our solution explorer should look as follows:
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:
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 :
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:
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:
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.
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 :
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:
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:
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.
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:
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.
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:
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.