Problem: We want to display multiple views within our page

Setup

You will need copies of the following files:

PLEASE NOTE: You must add a reference to the .d.ts file at the top of the typescript file


        ⁄⁄⁄ <path="typings/backbone.marionette/index.d.ts" ⁄>
    

Motivation

We would like to layout and manage multiple ItemViews, CollectionViews and CompositeViews on our webpage, a Marionette LayoutView allows us to define regions within which these views can be placed.

Usage

We will create a simple layout view to demonstrate the idea, but first lets create a Model and ItemView class.

namespace ExampleLayoutViewNS
{
    export class SomeRegionModel extends Backbone.Model 
    {
        constructor(id: number)
        {
            super({
                id: id      // Some id value so we can identify each model
            });
        }
        get Id(): number { return this.id; }
    }
    export class SomeRegionView extends Backbone.Marionette.ItemView<SomeRegionModel>
    {
        constructor(regionModel: SomeRegionModel)
        {
            super({
                model: regionModel,
                template: '#RegionTemplate'
            });
        }
    }
}

The webpage with regions located within it, looks like this:


    <!-- Using Underscore.js templating -->
    <script id="MainPageLayout" type="text/html">
        <div class="row-fluid">
            <div id="RegionOne">
            </div>
            <div id="RegionTwo">
            </div>
            <div id="RegionThree">
            </div>
            <div id="RegionFour">
            </div>
            <div id="RegionFive">
            </div>
        </div>
    </script>
    <script id="RegionTemplate" type="text/html">
        Hello from region <%= id %>
    </script>
    <!-- Display here -->
    <div id="container" class="well">
    </div>
    

We now define the LayoutView class, give the regions names and specify (using jQuery selectors) where the regions are located. For example, we will create a region_one and locate it at '#RegionOne' in the above HTML, etc.

namespace ExampleLayoutViewNS
{
    export class PageLayout extends Backbone.Marionette.LayoutView<SomeRegionModel>
    {
        constructor(el: any)
        {
            super({
                el: el,
                template: "#MainPageLayout",
                regions: {
                    region_one: '#RegionOne',
                    region_two: '#RegionTwo',
                    region_three: '#RegionThree'
                }
            });
        }
    }
}

You use this LayoutView like this:

class ExampleLayoutViewPage
{
    PageLoad(): void
    {
        let pageLayout: ExampleLayoutViewNS.PageLayout = new ExampleLayoutViewNS.PageLayout('#container');

        // Dynamically add a new region like this...
        pageLayout.addRegion("region_four", "#RegionFour");

        // Equivalent to pageLayout.region_four = new Backbone.Marionette.Region({el:"#container"});
        //...and remove it like this....
        //pageLayout.removeRegion("region_four");

        let regionOneModel: ExampleLayoutViewNS.SomeRegionModel = new ExampleLayoutViewNS.SomeRegionModel(1);
        let regionOneView: ExampleLayoutViewNS.SomeRegionView = new ExampleLayoutViewNS.SomeRegionView(regionOneModel);
        let regionTwoModel: ExampleLayoutViewNS.SomeRegionModel = new ExampleLayoutViewNS.SomeRegionModel(2);
        let regionTwoView: ExampleLayoutViewNS.SomeRegionView = new ExampleLayoutViewNS.SomeRegionView(regionTwoModel);
        let regionThreeModel: ExampleLayoutViewNS.SomeRegionModel = new ExampleLayoutViewNS.SomeRegionModel(3);
        let regionThreeView: ExampleLayoutViewNS.SomeRegionView = new ExampleLayoutViewNS.SomeRegionView(regionThreeModel);
        let regionFourModel: ExampleLayoutViewNS.SomeRegionModel = new ExampleLayoutViewNS.SomeRegionModel(4);
        let regionFourView: ExampleLayoutViewNS.SomeRegionView = new ExampleLayoutViewNS.SomeRegionView(regionFourModel);

        pageLayout.render();    // Must render the layout first to create the <div> tags for the regions

        // Call show() to display the view in the region i.e. make it the current view.
        // let someView = new SomeView();
        // let anotherView = new AnotherView();
        // footerRegion.show(someView);
        // footerRegion.show(anotherView); // Switch views, this destroys someView

        pageLayout.getRegion('region_one').show(regionOneView);
        pageLayout.getRegion('region_two').show(regionTwoView);
        pageLayout.getRegion('region_three').show(regionThreeView);
        pageLayout.getRegion('region_four').show(regionFourView);

        // pageLayout.destroy();   // Commenting this in causes the page to display nothing
     }
}
$(function ()
{
    let page: ExampleLayoutViewPage = new ExampleLayoutViewPage();
    page.PageLoad();
});

The behavior of the Destroy function on the Layout view is a little different as it will call destroy on all of the regions. These regions will then call destroy on the views that they contain, making sure all of the views contained are destroyed properly (i.e. closed). There are events available on the Region and View:

"before:show" / onBeforeShowCalled on the VIEW instance after the view has been rendered, but before its been displayed.
"before:show" / onBeforeShowCalled on the REGION instance after the view has been rendered, but before its been displayed.
"show" / onShowCalled on the VIEW instance when the view has been rendered and displayed.
"show" / onShowCalled on the REGION instance when the view has been rendered and displayed.
"before:swap" / onBeforeSwapCalled on the REGION instance before a new view is shown.
"swap" / onSwap NOTE: this will only be called when a view is being swapped, not when the region is empty.
Called on the REGION instance when a new view is shown.
NOTE: this will only be called when a view is being swapped, not when the region is empty.
"before:empty" / onBeforeEmptyCalled on the REGION instance before the view has been emptied.
"empty" / onEmptyCalled when the view has been emptied. (Equivalent to destroy/close)

We can create a Region class to handle events (although you don't have to handle any events) like this:

namespace ExampleLayoutViewNS
{
    export class CustomRegionWithEvents extends Backbone.Marionette.Region
    {
        constructor()
        {
            super({
                el: "#RegionFive"
            });
        }
        initialize(): void
        {
            window.alert("region 5 init");
        }
        onBeforeShow(view: SomeRegionView)
        {
            window.alert("About to open the view containing the model with id:" + view.model.get("id"));
        }
        onShow(view: SomeRegionView): void
        {
            window.alert("Opened and showing the view containing the model with id:" + view.model.get("id"));
        }
        onEmpty(view: SomeRegionView): void
        {
            window.alert("Closed the view containing the model with id:" + view.model.get("id"));
        }
    }
}

Could be used like this:

class ExampleLayoutViewPage
{
    PageLoad(): void
    {
        .......

        let regionFiveModel: ExampleLayoutViewNS.SomeRegionModel = new ExampleLayoutViewNS.SomeRegionModel(5.1 );
        let regionFiveView: ExampleLayoutViewNS.SomeRegionView = new ExampleLayoutViewNS.SomeRegionView(regionFiveModel);

        pageLayout.addRegion("region_five", new ExampleLayoutViewNS.CustomRegionWithEvents());

        //Can also do: pageLayout.addRegion("region_five", ExampleLayoutViewNS.CustomRegionWithEvents);
        // Generates ' begin:show' and 'show' events on the region. These events
        // are also generated on the "regionFiveView" view shown in the region but these are
        // not used in this example.
        pageLayout.getRegion('region_five').show(regionFiveView);

        let regionSixModel: ExampleLayoutViewNS.SomeRegionModel = new ExampleLayoutViewNS.SomeRegionModel(5.2);
        let regionSixView: ExampleLayoutViewNS.SomeRegionView = new ExampleLayoutViewNS.SomeRegionView(regionSixModel);

        // Switch the views.
        // Generates the 'empty' event for regionFiveView then the ' begin:show' 
        // and 'show' events on the regionSixView.
        pageLayout.getRegion('region_five').show(regionSixView);

        // Generates a 'empty' event on region 5.2
        pageLayout.removeRegion("region_five");
     }
}
$(function ()
{
    let page: ExampleLayoutViewPage = new ExampleLayoutViewPage();
    page.PageLoad();
});

Details

Continuing the previous exercise. Assume we wish to build a webpage that has the following layout.

List of books
Search for Book
Edit found book details
Add Book

When the user enters the name of a book in the search field and clicks a button, the details associated with the matching book should be displayed in a form below the search textbox so the user can edit and submit any changes required. You may fake the search process, i.e. just provide a Model object as if it was found. Should the user wish to add a new book, a form should be displayed below the list of a books. You may want to hard code a number of books into the Collection when the page loads. We will learn about event handling in the next lesson, so for now we won't be able to automatically update the views to display changes as we alter the Model details and Collection contents.

Hints

  1. You will need ItemViews and CollectionViews
  2. Add the views to the required Regions.