简体   繁体   中英

Set a Backbone collection model with circular dependencies in requirejs

The thing is that I have a circular dependecy between some Backbone modules so I have to use "exports" as Requirejs scpecifies in its documentation http://requirejs.org/docs/api.html#circular . So the model 'A' will look like this:

define(function(require, exports) {
  var B = require('B');
  var A = Backbone.Model.extend({

  });

  exports.model =  A;
});

And the collection 'B' like this:

define(function(require, exports) {
  var A = require('A');
  var B = Backbone.Model.extend({
    model: A.model
  });

  exports.model =  B;
});

The problem here is that by the time I have to specify the collection 'B' model property, the model 'A' isn't yet defined. This is the error I'm getting when I try to set the collection with models like this:

B.collection.set([{id: 1}, {id: 2}]);

Uncaught TypeError: 'undefined' is not an object (evaluating 'targetModel.prototype') (http://127.0.0.1:9999/bower_components/backbone/backbone.js:689)

Any ideas on how should I solve this problem?

From the example, it's not clear that B actually depends on A . If it's just a model:collection relationship, it might make sense to remove the dependency of the model on its collection. If it's at all possible to break the circular dependency, I would strongly encourage you to do so.

If the back-reference is truly required, though, one option might be to move the resources into the same module and do a sort of lazy export:

define(function() {

  var lazyThings = {
    A: null,
    B: null
  };

  lazyThings.A = Backbone.Model.extend({
    collection: things.B
  });

  lazyThings.B = Backbone.Collection.extend({
    model: A
  });

  return lazyThings;
});

Alternatively, you could return lazyThings.B and later access the model from its prototype:

require('b', function (B) {
  var A = B.prototype.model; // A
});

Finally, requirejs could be made to work by calling the respective dependencies lazily (ie, after the modules are resolved):

// B
define(['a'], function (A) {
  return function () {
    return Backbone.Collection.extend({
      model: A()
    });
  }
});

// A
define(['b'], function (B) {
  return function () {
    return Backbone.Model.extend({
      model: B()
    });
  }
});

The following works for me, try to make it clear as possible.

You have a model, you have a collection. In order for them to both depend on each other + avoid a circular dependency, you need a 3rd "mediator" dependency. It's convenient in Backbone to have a model and easily lookup what collection it belongs to, and vice versa, but the problem of course is they have a circular dependency.

So before we had:

+model
+collection
__________
= circular

and after:

+model
+collection
+mediator
________
= OK

//collection

define([

        '@allModels',
        '@BaseCollection',
        '@AppDispatcher',
        '@allFluxConstants',
        'app/js/flux/flux-helpers/collectionUpdater'
    ],

    function (allModels, BaseCollection, AppDispatcher, allFluxConstants, collUpdater) {


        var dispatchCallback = function (payload) {
            return true;
        };


        var BaymaxComponentCollection = BaseCollection.extend({


            model: allModels['BaymaxComponent'],

            collectionName:'baymax-component',

            url: '/baymax_component',

            batchURL: '/batch/baymax_component',


            initialize: function (models, opts) {

                this.dispatchToken = AppDispatcher.register(dispatchCallback);


            },

            // collection is sorted by original insertion order.
            comparator: 'order'
        });


        return new BaymaxComponentCollection();
    });

//model

define([

        '@BaseModel',
        '@ModelCollectionMediator',
        '@AppDispatcher'

    ],

    function ( BaseModel, MCM) {

        var BaymaxComponent = BaseModel.extend({

                idAttribute: 'id',
                urlRoot: '/baymax_component',
                collectionName: 'baymax-component',

                defaults: function () { //prevents copying default attributes to all instances of UserModel
                    return {}
                },

                initialize: function (attributes, opts) {

                    //*** the following line is crucial ***
                    this.collection = MCM.findCollectionByName(this.collectionName);

                },


                validate: function (attr) {

                    return undefined;
                }
            },

            { //class properties

            });

        return BaymaxComponent;
    });

//mediator

define(function (require) {

    return {
        findCollectionByName: function (name) {
            var allCollections = require('@allCollections');
            return allCollections[name];
        }
    };

});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM