Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Page transitions with RequireJS and Backbone.js

I'm developing a mobile application using RequireJS and Backbone.js. I'd like to specify the transition from one page to another by adding data-transition and data-direction attributes to each anchor (in the same way as with jQuery Mobile):

<a href="#home" data-transition="slide" data-direction="left">Go to the home page</a>

All my views extend a base view that attaches a click handler to anchors and catches the values of these attributes:

define([
    'zepto',
    'lodash',
    'backbone'
], function ($, _, Backbone) {
    'use strict';

    BaseView = Backbone.View.extend({
        events: {
            'click a': 'navigate'
        },
        navigate: function (e) {
            e.preventDefault();

            var href = $(e.currentTarget).attr('href'),
                transition = $(e.currentTarget).attr('data-transition'),
                direction = $(e.currentTarget).attr('data-direction');

            Backbone.history.navigate(href, true);
        }
    });
});

My problem is that I don't know how to pass these values to the route handlers of my router, defined as another module:

define([
    'zepto',
    'lodash',
    'backbone'
], function ($, _, Backbone) {
    'use strict';

    var Router = Backbone.Thumb.Router.extend({
        routes: {
            '': 'home'
        }
    });

    var initialize = function () {
        var router = new Router();

        router.on('route:home', function () {
            require(['views/home'], function (HomeView) {
                var homeView = new HomeView();

                // Get the data-transition and data-direction attributes
                // ​​of the clicked anchor here:

                // var transition = ???,
                //     direction = ???;

                router.animate(homeView.render().$el, transition, direction);
            });
        });

        Backbone.history.start();
    };

    return {
        initialize: initialize
    };
});

Does anyone know a good solution to this problem?

Thank you very much in advance! :-) Best regards,

David

like image 543
davidg Avatar asked Jul 13 '13 22:07

davidg


1 Answers

Events

As you may know, Backbone.js is an event driven framework. Every object it defines inherits from the Backbone.Events object and can send and receive event messages. That means is that the router itself can listen to events.

Using global messages

Since version 0.9.2, the Backbone global object itself can be used as a mediator for global messenging. Since a view object in the application might not know about the router (it is especially true when using requireJS modules), it is possible to enable communication between these objects using a Mediator.

Example of a router listening to a global event:

var router = Backbone.Router.extend({
  initialize: function() {
    this.listenTo( Backbone, 'page-transition', this.animate );
  },
  animate: function( href, transition, direction ) {
    // Do something interesting with this
  }
});

What is hapenning here?

  1. The router registers its own animate function in the Backbone.events['page-transition'] stack when it is instantiated.
  2. When the Backbone object triggers the page-transition event, it will call the router.animate function with the arguments provided with the event trigger.

Where do I trigger the event?

From anywhere in the application.

How do I trigger the event?

Here is an example based on the code from your question:

BaseView = Backbone.View.extend({
  events: {
    'click a': 'transition'
  },
  transition: function (e) {
    e.preventDefault();
    var href = $(e.currentTarget).attr('href'),
        transition = $(e.currentTarget).attr('data-transition'),
        direction = $(e.currentTarget).attr('data-direction');

    Backbone.trigger('page-transition', href, transition, direction );
  }
});

Since your router has already registered to the page-transition event from the Backbone object, it will call the router.animate function with the proper arguments.

Conclusion

This pattern can be used everywhere in your Backbone application, these events can be listened by any Backbone extended object, may it be a Collection, Model, View, Router... You can even create a special mediator with this one liner:

var mediator = _.extend({}, Backbone.Events);

This pattern is very powerful because it promotes full decoupling between modules. Your modules don't have to know who is handling the functionality, they just have to know that it is not their responsibility and warn the application about it by triggering an event.

like image 53
mor Avatar answered Oct 25 '22 20:10

mor