Backbone – How to manage collection initialization when users access website through specific post?

I’m trying to create a WordPress theme using Javascript, Requirejs and Backbonejs. The entire theme is based on slide transitions between each routes.

The home page displays a wallView which contains an albumCoverView for each elements of an albumsCollection. When a user clicks on an albumCoverView, he navigates to the corresponding post url (firing album route) and slide transition between wallView and albumView starts.

Read More

Here is the code of my router.js file

define( function( require, exports, module ) {

"use strict";

var app     = require( 'app' );
var cache   = require( 'backbone-cache' );
var Albums  = require( 'modules/albums/index' );
var Wall    = require( 'modules/wall/index' );

var Router = Backbone.Router.extend( {

    routes: {
        ''                          : 'index',
        ':year/:month/:day/:slug/'  : 'album'
    },

    index: function() {

        if( typeof app.albumsCollection == "undefined" ) {

            app.albumsCollection = new Albums.Collection();

            app.albumsCollection.fetch( {

                cache: true,

                success: function() {

                    app.wallView = new Wall.View( {

                        collection: app.albumsCollection

                    } );

                    app.wallView.render();

                    app.wallView.show();

                },

                error: function() {

                    console.log( "Collection: modules/albums -> fetch failed" );

                }

            } );

        }

        else app.wallView.show();

    },

    album: function( year, month, day, slug ) {

        var albumModel = app.albumsCollection.findWhere( { post_name: slug } );

        if( albumModel.get( 'format' ) == "gallery" ) {

            app.albumView = new Albums.Views.Gallery( {

                id      : "album-" + albumModel.get( 'ID' ),
                model   : albumModel

            } );

        }

        else if( albumModel.get( 'format' ) == "image" ) {

            app.albumView = new Albums.Views.Image( {

                id      : "album-" + albumModel.get( 'ID' ),
                model   : albumModel

            } );

        }

        app.albumView.render();

        app.albumView.show();

    }

} );

module.exports = Router;

} );

And here, the code of my modules/albums/collection.js file

define( function( require, exports, module ) {

"use strict";

var app         = require( 'app' );
var AlbumsModel = require( './model' );

var AlbumsCollection = Backbone.Collection.extend( {

    model   : AlbumsModel,
    url     : app.jsonApi + "albums/get_albums/",

    parse: function( response ) {

        return response.posts;

    }

} );

module.exports = AlbumsCollection;

} );

Slide transitions are managed by the .show() function of the view which will be display.

My Problem :

I’m fetching WordPress posts (using the JSON API plugin) and caching them in localStorage (using backbone-fetch-cache) when the user is on the home page.

With this method, the problem is : when a user access the website through a link to a specific post, the albumsCollection is undefined and so, the application can’t find the corresponding album datas (var albumModel = app.albumsCollection.findWhere( { post_name: slug } );). So, I would like to know how to solve this problem ?

  • Do I need to bootstrap albumsCollection using this method : Loading Bootstrapped Models ?
  • Do I need to fetch the albumsCollection on router initialization and use $.when(…).then(…); in each routes ?
  • Do I need to create a get_album method into my json controller to fetch only one album and then reset the albumsCollection to repopulate when the user go to the home page ?

Any advice will be welcome.

Related posts

Leave a Reply

1 comment

  1. I’ve been developing a large javascript application for the last months, and you are running into a common problem.

    My solution is really simple: whenever you need a shared collection/model, check if it is loaded, fetching it if it is not loaded. And, after assuring it is loaded, proceed to whatever you want to do with it.

    To keep shared models/collections, create stores where you can keep those objects for whenever you need them. In this case, my store is my ‘static’ _instance variable.

    If memory leaks concern you, I would recommend using a count for each object at the stores, so you can reap zero-count models/collections.

    For example:

    var AlbumsCollection = Backbone.Collection.extend( {
    
        model   : AlbumsModel,
        url     : app.jsonApi + "albums/get_albums/",
    
        initialize: function ( options ) {
    
            this.loaded = false;
    
            this.listenToOnce('sync', this.onLoaded);
    
        },
    
        onLoaded: function () {
    
            if (!this.loaded) {
    
                 this.loaded = true;
    
                 this.trigger('loaded', this);
    
            }
    
        },   
    
        parse: function( response ) {
    
            return response.posts;
    
        }
    
     },
     // Static part
     {
    
         _instance: null,
    
         getInstance: function () {
    
             if (!this._instance)
                  this._instance = new AlbumsCollection();
    
             return this._instance;
    
         }    
    
    });
    

    So, in your router/view:

    function ... () {
    
         var collection = AlbumsCollection.getInstance();
    
          if (collection.loaded)
              this.onAlbumsLoaded(collection);
          else {
    
              this.listenToOnce('loaded', this.onAlbumsLoaded);
    
              collection.fetch();
    
         }
    } 
    

    And you just do what you have to do with your collection inside your onAlbumsLoaded function.

    Hope I’ve helped.