3
votes

Even after reading some nice tutorials and blogposts on how to structure a Backbone Marionette app, I got confused while writing my own. Here is my setup.

I have an app that can easily be structured into different sub-apps (aka Backbone modules) Click on a link in my main navigation bar starts one of these apps. That means it loads the apps starting layout into my #main div.

But these apps have different layouts in themself, so navigating inside the app overwrites regions specified in the main app layout.

e.g.

var myApp = Backbone.Marionette.Application()
...
var layout = myApp.myModule.layout = new Backbone.Marionette.Layout()
...
myApp.main.show(myApp.myModule.layout)

where layout has the following DOM tree each mapped to a region

#app_main
  .albums
  .artists

Then I do something like

layout.app_main.show(new myView())

and from now on I can not get access to layout.albums or layout.artists even after using the back button (using a Backbone.Router and History)

Should I split my module's main layout to contain only #app_main, and load the opening layout into it at a separate step? Or do you have any other ideas?

2
Use events to communicate through the views. Raise these events on the models ( so that you can even subscribe to them through collection)Deeptechtons

2 Answers

3
votes

It would help to know a little bit more about what you're going for, but here's a shot at an answer. Let me know if it helps!

Let's say this is your HTML layout:

<div id="app">
  <div id="app_nav">
    <a href="#music">Music</a>
    <a href="#books">Books</a>
  </div>
  <div id="sub_app"></div>
</div> <!-- /#app_main -->

For our "Music" sub-app, we'll use this template:

<script id="musicApp-template" type="template/html">
  <div class="albums"></div>
  <div class="artists"><div>
</script>

For the album item view:

<script id="album-template" type="template/html">
  <img src="<%=albumThumbSrc %>" />
  <p><%=albumTitle %></p>
</script>

For the artist item view:

<script id="artist-template" type="template/html">
  <%=firstName %> <%=lastName %>
</script>

--

For our "Books" sub-app, we'll use this template:

<script id="booksApp-template" type="template/html">
  <div class="books"></div>
  <div class="authors"></div>
</script>

For the book item view:

<script id="book-template" type="template/html">
  <img src="<%=bookThumbSrc %>" />
  <p><%=bookTitle %></p>
</script>

For the artist item view:

<script id="author-template" type="template/html">
  <%=firstName %> <%=lastName %>
</script>

And to bootstrap the app:

$(document).ready({
  myApp.start();
  if(!Backbone.history.started) Backbone.history.start();
});

--

Now to set up our Marionette views.

In myApp.js

var myApp = new Backbone.Marionnette.Application();

myApp.addRegions({
  subAppRegion: "#sub_app"
});

// Your data
myApp.artists = new Backbone.Collection([...]);
myApp.books = new Backbone.Collection([...]);

In myApp.musicApp.js

myApp.module("musicApp", function(musicApp, myApp) {
  /* Setup your Views
   ================== */
  var MusicLayout = Backbone.Marionette.Layout.extend({
    template: #musicApp-template",
    regions: {
      albumsRegion: ".albums",
      artistsRegion: ".artists"
    }
  });

  var AlbumView = Backbone.Marionette.ItemView.extend({
    template: "#album-template"
  });

  var AlbumListView = Backbone.Marionette.CollectionView({
    itemView: AlbumView
  });

  var ArtistView = ...
  var ArtistListView = ...

  /* Create router for app navigation
   ==================================== */
  var Router = Backbone.Marionette.AppRouter.extend({
    appRoutes: {
      "music" : showMusicApp
    },
    controller: musicApp
  });
  new Router();

  /* Method to show the music app
   ================== */
  musicApp.showMusicApp = function() {
    // Instantiate Views
    var musicLayout = new MusicLayout();
    var albumListView = new AlbumListView({ collection: myApp.albums });
    var artistListView = new ArtistsListView({ collection: myApp.artists });

    // Show musicApp layout in subAppRegion of main app
    myApp.subAppRegion.show(musicLayout);

    // Show albums and artists in musicApp layout
    layout.albumsRegion.show(albumListView);
    layout.artistsRegion.show(artistListView);
  }
});

You can set up your myApp.booksApp module much the same way:

myApp.module("booksApp", function(booksApp, myApp) {
  ...
  var Router = Backbone.Marionette.AppRouter.extend({
    appRoutes: {
      "books" : showBooksApp
    },
    controller: booksApp
  });
  new Router();

  booksApp.showBooksApp = function() {
    ...
    myApp.subAppRegion.show(booksLayout)
    ...
  }
  ...
});

I haven't tested all this code, so sorry if there are some kinks, and I'm sure it could be improved.

If you haven't already read David Sulc's tutorial, you should take a look - it will give you a better idea of a full fledged application. But I hope this gives you a basic idea of how you use layouts and regions to show different subApp views.

0
votes

As a general rule in my own projects, I always keep elements defining regions empty and then load them dynamically in the Layout.onRender method. This has the added benefit in Marionette.Async that if I need to load additional data from the server before displaying the view in the region, I can do so in the view's beforeRender method.

One exception to my rule is showing an animated "loading..." div in the region's element which gets overwritten as soon as populate the region.