web-dev-qa-db-fra.com

Utilisation du routeur Backbone.js pour naviguer dans les vues modularisées avec require.js

Je sépare mes vues et le routeur dans des fichiers séparés avec require. J'ai ensuite un fichier main.js qui instancie le routeur et rend également ma vue par défaut.

Mon routeur a les options view ('View /: id') et edit ('Edit /: id'). Dans main.js, lorsque j'instancie le routeur, je peux coder en routeur router.navigate ('View/1', true) et la navigation fonctionne correctement. Dans mon fichier de vue, lorsque je clique sur le lien de modification, je souhaite appeler router.navigate ('View /' + id, true), mais je ne suis pas sûr de savoir comment procéder.

J'ai eu du succès à appeler Backbone.history.navigate ('View /' + id, true), mais je ne pense pas devoir me fier à l'objet global Backbone.

J'ai essayé de passer ({routeur: appRouter}) à mes vues pour pouvoir utiliser this.options.router.navigate (), mais cela ne fonctionnait pas pour moi.

Au cas où vous seriez curieux, voici un tas de code de mon application:

Routeur:

define(['./View', './Edit'], function (View, Edit) {
    return Backbone.Router.extend({
        routes: {
            'View/:id': 'view',
            'Edit/:id': 'edit'
        },

        view: function (id) {
            var model = this.collection.get(id);
            var view = new View({ model: model });
            view.render();
        },

        edit: function (id) {
            var model = this.collection.get(id);
            var edit = new Edit({ model: model });
            edit.render();
        }
    });
});

Vue:

define(function () {
    return Backbone.View.extend({
        template: Handlebars.compile($('#template').html()),

        events: {
            'click .edit': 'edit'
        },

        render: function () {
            //Create and insert the cover letter view
            $(this.el).html(this.template(this.model.toJSON()));
            $('#View').html(this.el);

            return this;
        },

        edit: function () {
            Backbone.history.navigate('Edit/' + this.model.id, true); 
        },
    });
});
35
MrGrigg

Comme avec à peu près toutes les questions de Backbone, il existe de nombreuses façons de gérer cela. Dans le cadre de mon projet actuel, j'ai abordé le problème de manière à tout mettre dans un espace de noms personnalisé global et à l'utiliser pour transmettre les références nécessaires:

var MyNamespace = {};

MyNamespace.init = function() {
    MyNamespace.appView = new MyAppView();
    MyNamespace.router = new MyRouter();
    // etc
}

Les vues pourraient alors faire référence à MyNamespace.router si nécessaire. Mais il semble que cela ne fonctionnera pas/n'est pas encouragé avec require.js, alors voici quelques autres options:

  • N'appelez jamais explicitement le routeur. Modifiez plutôt un objet d'état global écouté par le routeur. C’est en fait ainsi que j’ai fait les choses dans mon projet actuel - voir cette réponse pour plus de détails.

  • Connectez le routeur à votre vue de niveau supérieur, souvent appelée AppView, rendez-le accessible de manière globale et utilisez AppView.router.navigate().

  • Créez un autre module qui fournit une fonction utilitaire navigate qui appelle Backbone.history.navigate() en interne. Ce n'est pas très différent de ce que vous faites, mais cela le rendrait légèrement plus modulaire et vous empêcherait d'utiliser la référence globale tout le temps. Cela vous permet également de modifier l'implémentation interne.

20
nrabinowitz

Si quelqu'un d'autre cherche une solution à ce problème comme je le faisais, je poste ce que j'ai finalement fait. Si vous utilisez le fichier backbone.js, vous aurez une fonction initialize() dans router.js. J'ai modifié ma fonction initialize() pour ressembler à ceci:

initialize = function () {
  var app_router;
  app_router = new AppRouter();

  // Extend the View class to include a navigation method goTo
  Backbone.View.goTo = function (loc) {
    app_router.navigate(loc, true);
  };

  Backbone.history.start();
};

En raison de l'héritage particulier de backbone.js, cela me permet d'appeler MyView.goTo(location); à partir de n'importe lequel de mes points de vue.

32
Horatio Alderaan

Vous pouvez le faire à l'ancienne avec window.location.hash :)

window.location.hash = "Edit/1"

Voici une solution alternative si vous n'avez pas besoin de routes explicites. Lorsque votre application démarre, créez un objet qui étend les événements de réseau principal

window.EventDispatcher = _.extend({}, Backbone.Events);

Ensuite, n'importe où dans votre application, vous pouvez écouter des événements

EventDispatcher.bind("mymodel:edit", this.editHandler, this);

Et aussi de n'importe où envoyer l'événement, data ci-dessous sont tous les paramètres que vous souhaitez envoyer pour le trajet

EventDispatcher.trigger("mymodel:edit", data);
8
kreek

Pour moi, la solution avec la fonction goTo a fonctionné avec un léger changement 

 Backbone.View.prototype.goTo = function (loc) {
      appRouter.navigate(loc, true);
    };
5
john

Je sais que cette question est ancienne, mais je me demande pourquoi vous n'avez pas utilisé require.js pour obtenir le routeur:

define(['./PathToRouter', ], function (router) {
    return Backbone.View.extend({
        template: Handlebars.compile($('#template').html()),

        events: {
            'click .edit': 'edit'
        },

        render: function () {
            //Create and insert the cover letter view
            $(this.el).html(this.template(this.model.toJSON()));
            $('#View').html(this.el);

            return this;
        },

        edit: function () {
            router.navigate('Edit/' + this.model.id, true);
        }
    });
});
3
Naor

Qu'en est-il de cette approche? Au fur et à mesure que Backbone implémente le modèle de template dans ses 4 composants, avec un peu de design, vous pouvez fournir à chaque vue un mécanisme de navigation facile à travers le routeur de l'application, sans avoir à faire de référence circulaire essayez de l'éviter).

Composant routeur, pas trop différent des autres exemples de routeur:

define('Router', ['backbone', ... ],
        function (Backbone, ...) {

            var MyRouter = Backbone.Router.extend({
                routes: {
                    'viewA': 'viewA',
                    'viewB': 'viewB'
                },

                initialize: function () {
                    ...
                };
            },
            viewA: function () {
                ...
            },

            viewB: function () {
                ...
            }
});

return MyRouter;
});

App, crée l'instance de routeur et déclenche la première vue transmettant cette instance:

define('App', ['backbone', ...
], function (Backbone, ...) {

    function initialize() {

        //route creation
        if (!this.routes)
            routes = new Router(this); 
        //backbone history start
        Backbone.history.start();

        //ViewA navigation, bigbang
        if (!this.viewA)
            this.viewA = new ViewA({router: this.routes});
        this.viewA.render();
    }

    return {
        initialize: initialize
    };
});

BaseView, définition du constructeur de base pour toutes les vues et méthode de navigation:

define('BaseView', ['jquery', 'underscore',  'backbone', ...
], function ($, _, Backbone, ...) {
    var BaseView;

    BaseView = Backbone.View.extend({
        id: '...',

        constructor: function (options) {
            this.router = options.router;
            this.model = options.model;
            Backbone.View.prototype.constructor.call(this);
        },
        initialize: function () {
            this.template = _.template(tpl);
        },

        events: {

        },
        render: function () {
            $(this.el).html(this.template());

            return this;
        },
        //Provides transparent navigation between views throught the backbonejs
        //route mechanism
        navigate: function(pageId)
        {
            this.router.navigate(pageId, {trigger: true});
        }
    });

    return BaseView;
});

Une instance de View, chaque vue s'étend de la base à la place du backbone et hérite du comportement de base:

define('ViewA', ['jquery', 'underscore',  'backbone', 'BaseView'
], function ($, _, Backbone, BaseView) {
    var ViewA;

    ViewA = BaseView.extend({
        id: '...',

        constructor: function (options) {
            this._super("constructor");
        },

        ...
        foo: function()
        {
            ...

            this.navigate("viewB");
        }
    });

    return ViewA;
});

Cela fonctionne pour moi et peut également être réutilisé dans d'autres projets.

2
matiasnj

pour moi, j'ai ajouté un objet à l'application principale comme ceci; 

define(['jquery','underscore','backbone','views/personView','views/peopleView','views/textView'],function($,_,backbone,PersonView,PeopleView,TitleView){

    var Router = Backbone.Router.extend({
           routes:{
               '':'home',
               'edit/:id':'editPerson',
               'new':'editPerson',
               'delete/:id':'deletePerson'
               }
            })

              var initialize = function(){

                 var router  = new Router();

                 window.app = {
                     router:router
                     }

        router.on('route:home',function(){


    })

            //enable routing using '#'[hashtag] navigation
        Backbone.history.start();

            };

            return {
            initialize:initialize
            };

});

et dans votre vue, vous pouvez dire windows.app.router.navigate ({'', trigger: true}). Je ne sais pas si l'étendue mondiale est une bonne pratique dans ce cas, mais cela a fonctionné pour moi.

1
user1975073

J'ai une nouvelle solution pour le routage des modules AMD.

RequireJS Router https://github.com/erikringsmuth/requirejs-router

Cela vous permet de charger paresseux des modules AMD lorsque vous naviguez vers chaque page. Avec le routeur Backbone, vous devez exiger toutes vos vues en tant que dépendances. Cela charge toutes vos applications Javascript sur la première page de chargement. Le routeur RequireJS lazy charge les modules lorsque vous naviguez vers chaque itinéraire.

Exemple main.js utilisé pour exécuter votre application

define([], function() {
  'use strict';

  // Configure require.js paths and shims
  require.config({
    paths: {
      'text': 'bower_components/requirejs-text/text',
      'router': 'bower_components/requirejs-router/router'
    }
  });

  // Load the router and your layout
  require(['router', 'js/layout/layoutView'], function(router, LayoutView) {
    var layoutView = new LayoutView();
    // The layout's render method should draw the header, footer, and an empty main-content section
    // then load the content section.
    // render: function() {
    //   this.$el.html(this.template({model: this.model}));
    //   router.loadCurrentRoute();
    // }

    // Configure the router
    router
      .registerRoutes({
        home: {path: '/', moduleId: 'home/homeView'},
        order: {path: '/order', moduleId: 'order/orderView'},
        notFound: {path: '*', moduleId: 'notFound/notFoundView'}
      })
      .on('statechange', function() {
        // Render the layout before loading the current route's module
        layoutView.render.call(layoutView);
      })
      .on('routeload', function(module, routeArguments) {
        // Attach the content view to the layoutView's main-content section
        layoutView.$('#main-content').replaceWith(new module(routeArguments).render().el);
      })
      .init({
        // We're manually calling loadCurrentRoute() from layoutView.render()
        loadCurrentRouteOnStateChange: false
      });
  );
);
0
Erik Ringsmuth