6
\$\begingroup\$

My app module bootstraps a Backbone.js application.
I need app to be available in every other view for easy access to router, triggering navigation, etc.

I never ported an app to RequireJS before, and I got stuck on circular dependency problem, and this answer isn't quite clear to me, so I'm not sure if I applied the solution correctly.

I'm using AMD sugar style throughout the app, so here's what I came up with:

app.js

define(function (require, exports) {

  'use strict';

  var _ = require('underscore'),
      Backbone = require('backbone'),
      Router = require('Router'),
      AppView = require('common/views/app_view'),

  // ...

  return _.extend(exports, {
    start: function () {
      this.router = new Router();
      Backbone.history.start();

      this.appView = new AppView();
      this.appView.render();
    },

    // ...
  });

});

Extending exports instead of directly returning an object allows me to do app = require('app') in any view of the app without worrying about circular dependencies.

All other modules in the app return value normally (i.e. I'm not using exports anywhere else).

Is this an OK strategy for a Require.js Backbone app?
Are there better solutions (that will also work with optimizer or Almond)?

An example of how I'm using app in a (rather deeply nested) view:

zine_item_view.js

define(function (require) {

  'use strict';

  var View = require('common/views/view'),
      Misc = require('common/helpers/misc'),
      app = require('app');

  var ZineItemView = View.extend({
    template: 'templates/social/items/zine_item.html',

    events: {
      'click': 'showZine'
    },

    showZine: function (e) {
      if (!Misc.shouldHandleClick(e)) {
        return;
      }

      app.presenter.showZine({
        zine: this.model,
        route: app.router.routeToZineTrending(this.model.id)
      });
    }
  });

  return ZineItemView;

});
\$\endgroup\$
4
  • \$\begingroup\$ From an MVC perspective, your views should not have access to router or be able to trigger navigation. Don't get me wrong, I employ a similar scheme, but I think this is wrong. \$\endgroup\$
    – konijn
    Jan 28, 2014 at 14:38
  • \$\begingroup\$ Can you also post one of your views that would use app ? \$\endgroup\$
    – konijn
    Jan 28, 2014 at 14:50
  • \$\begingroup\$ Sorry if I am dense, but what is app.presenter.showZine supposed to do here ? Are you setting a route inside of a View ? \$\endgroup\$
    – konijn
    Jan 29, 2014 at 4:21
  • \$\begingroup\$ @konijn: presenter is not a view, it's a separate object that has methods like showZine, showUser, showHome, etc. Think of it as of a simple “controller” (although there are no controllers in Backbone per se). It manages the root view. It often needs to load a model asynchronously, and, when it is ready, updates both root view and the current route. \$\endgroup\$
    – Dan
    Jan 29, 2014 at 10:23

1 Answer 1

5
+50
\$\begingroup\$

There are few ways to solve your problem with Backbone.js + Require.js and executing functions or changing routes from view.

1. Using Backbone.Router

Here is approach I've used multiple times:

'use strict';

// Your view --------------
define([
    'jquery'
    'backbone'
    'common/views/view',
    'common/helpers/misc'
], function ($, Backbone, View, Misc) {

    var ZineItemView = View.extend({
        ...
        showZine: function (e) {
          if (!Misc.shouldHandleClick(e)) {
            return;
          }
          // Pass all control from view to the router,
          // see http://stackoverflow.com/a/20311253/1363799
          Backbone.history.navigate('yourUrl_to_zine');
        }
    });

    return ZineItemView;
});

// Main block --------------
require([
    'jquery'
    'backbone',
    'app',
    'ZineItemView'
], function($, Backbone, App, ZineItemView) {
    ...

    var app = new App();

    var zineItemView = new ZineItemView({...});

    // Router way       
    var router = Backbone.Router.extend({
      routes: {
        "yourUrl_to_zine": "showZine"
      },
      showZine: function() {
            // Here you gave access to zineItemView.model and zineItemView.model.id
            app.presenter.showZine({
                zine: zineItemView.model,
                // I think next row becames obsolete
                route: app.router.routeToZineTrending(zineItemView.model.id)
            });
        }
    });
});

2. Event-driven approach

'use strict';

// Your view --------------
define([
    'jquery',
    'backbone',
    'common/views/view',
    'common/helpers/misc'
], function ($, Backbone, View, Misc) {

    var ZineItemView = View.extend({
        ...
        showZine: function (e) {
          if (!Misc.shouldHandleClick(e)) {
            return;
          }

          var view = this;

          // Trigger global event with desired parameters
          $(window).trigger('showZine.App', {
            zine: view.model,
            modelId: view.model.id // probably it's obsolete, because you can get it from zine property
          });
        }
    });

    return ZineItemView;
});

// Somewhere in app block --------------
// lodash is a dependency, because we use bind method
var App = {
    initialize: function() {
        $(window).on('showZine.App', _.bind(this.presenter.showZine, this) );
    },
    presenter: {
        showZine: function(event, paramsObj) {
            // Here you have paramsObj,
            // with paramsObj.zine = zineItemView.model
            // and paramsObj.modelId = zineItemView.model.id

            // Call app.router.routeToZineTrending from here, but be aware about value of `this` and `app` variables
        }
    }
};

3. Syntax of module defining (optional)

It preferentual, but seems to be a standart (it's easier to see dependencies):

'use strict';

define([
    'common/views/view',
    'common/helpers/misc',
    'app'
], function (View, Misc, app) {

    var ZineItemView = View.extend({
        ...
    });

    return ZineItemView;
});
\$\endgroup\$
1
  • \$\begingroup\$ You are welcome :). I'm also looking for more robust solution which somebody could propose. \$\endgroup\$
    – terales
    Apr 7, 2014 at 12:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.