zoukankan      html  css  js  c++  java
  • backbone.js 1.3.3----------------------------------------collection

    collection就是model的集合,这里提供各种操作集合的方法。可以参照api。

    fetch方法成功后,有两个回调函数可供使用,reset和set。

      1 // Backbone.Collection
      2   // -------------------
      3 
      4   // If models tend to represent a single row of data, a Backbone Collection is
      5   // more analogous to a table full of data ... or a small slice or page of that
      6   // table, or a collection of rows that belong together for a particular reason
      7   // -- all of the messages in this particular folder, all of the documents
      8   // belonging to this particular author, and so on. Collections maintain
      9   // indexes of their models, both in order, and for lookup by `id`.
     10 
     11   // Create a new **Collection**, perhaps to contain a specific type of `model`.
     12   // If a `comparator` is specified, the Collection will maintain
     13   // its models in sort order, as they're added and removed.
     14   var Collection = Backbone.Collection = function(models, options) {
     15     options || (options = {});
     16     this.preinitialize.apply(this, arguments);
     17     if (options.model) this.model = options.model;
     18     if (options.comparator !== void 0) this.comparator = options.comparator;
     19     this._reset();
     20     this.initialize.apply(this, arguments);
     21     if (models) this.reset(models, _.extend({silent: true}, options));
     22   };
     23 
     24   // Default options for `Collection#set`.
     25   var setOptions = {add: true, remove: true, merge: true};
     26   var addOptions = {add: true, remove: false};
     27 
     28   // Splices `insert` into `array` at index `at`.
     29   var splice = function(array, insert, at) {
     30     at = Math.min(Math.max(at, 0), array.length);
     31     var tail = Array(array.length - at);
     32     var length = insert.length;
     33     var i;
     34     for (i = 0; i < tail.length; i++) tail[i] = array[i + at];
     35     for (i = 0; i < length; i++) array[i + at] = insert[i];
     36     for (i = 0; i < tail.length; i++) array[i + length + at] = tail[i];
     37   };
     38 
     39   // Define the Collection's inheritable methods.
     40   _.extend(Collection.prototype, Events, {
     41 
     42     // The default model for a collection is just a **Backbone.Model**.
     43     // This should be overridden in most cases.
     44     model: Model,
     45 
     46 
     47     // preinitialize is an empty function by default. You can override it with a function
     48     // or object.  preinitialize will run before any instantiation logic is run in the Collection.
     49     preinitialize: function(){},
     50 
     51     // Initialize is an empty function by default. Override it with your own
     52     // initialization logic.
     53     initialize: function(){},
     54 
     55     // The JSON representation of a Collection is an array of the
     56     // models' attributes.
     57     toJSON: function(options) {
     58       return this.map(function(model) { return model.toJSON(options); });
     59     },
     60 
     61     // Proxy `Backbone.sync` by default.
     62     sync: function() {
     63       return Backbone.sync.apply(this, arguments);
     64     },
     65 
     66     // Add a model, or list of models to the set. `models` may be Backbone
     67     // Models or raw JavaScript objects to be converted to Models, or any
     68     // combination of the two.
     69     add: function(models, options) {
     70       return this.set(models, _.extend({merge: false}, options, addOptions));
     71     },
     72 
     73     // Remove a model, or a list of models from the set.
     74     remove: function(models, options) {
     75       options = _.extend({}, options);
     76       var singular = !_.isArray(models);
     77       models = singular ? [models] : models.slice();
     78       var removed = this._removeModels(models, options);
     79       if (!options.silent && removed.length) {
     80         options.changes = {added: [], merged: [], removed: removed};
     81         this.trigger('update', this, options);
     82       }
     83       return singular ? removed[0] : removed;
     84     },
     85 
     86     // Update a collection by `set`-ing a new list of models, adding new ones,
     87     // removing models that are no longer present, and merging models that
     88     // already exist in the collection, as necessary. Similar to **Model#set**,
     89     // the core operation for updating the data contained by the collection.
     90     // 1.解析这次的操作add,merge,remove,sort
     91     // 2.执行操作
     92     // 3.判断silent,执行事件
     93     set: function(models, options) {
     94       if (models == null) return;
     95 
     96       options = _.extend({}, setOptions, options);
     97       if (options.parse && !this._isModel(models)) {
     98         models = this.parse(models, options) || [];
     99       }
    100 
    101       var singular = !_.isArray(models);
    102       models = singular ? [models] : models.slice();
    103 
    104       var at = options.at;
    105       if (at != null) at = +at;
    106       if (at > this.length) at = this.length;
    107       if (at < 0) at += this.length + 1;
    108 
    109       var set = [];
    110       var toAdd = [];
    111       var toMerge = [];
    112       var toRemove = [];
    113       var modelMap = {};
    114 
    115       var add = options.add;
    116       var merge = options.merge;
    117       var remove = options.remove;
    118 
    119       var sort = false;
    120       var sortable = this.comparator && at == null && options.sort !== false;
    121       var sortAttr = _.isString(this.comparator) ? this.comparator : null;
    122 
    123       // Turn bare objects into model references, and prevent invalid models
    124       // from being added.
    125       var model, i;
    126       for (i = 0; i < models.length; i++) {
    127         model = models[i];
    128 
    129         // If a duplicate is found, prevent it from being added and
    130         // optionally merge it into the existing model.
    131         var existing = this.get(model);
    132         if (existing) {
    133           if (merge && model !== existing) {
    134             var attrs = this._isModel(model) ? model.attributes : model;
    135             if (options.parse) attrs = existing.parse(attrs, options);
    136             existing.set(attrs, options);
    137             toMerge.push(existing);
    138             if (sortable && !sort) sort = existing.hasChanged(sortAttr);
    139           }
    140           if (!modelMap[existing.cid]) {
    141             modelMap[existing.cid] = true;
    142             set.push(existing);
    143           }
    144           models[i] = existing;
    145 
    146         // If this is a new, valid model, push it to the `toAdd` list.
    147         } else if (add) {
    148           model = models[i] = this._prepareModel(model, options);
    149           if (model) {
    150             toAdd.push(model);
    151             this._addReference(model, options);
    152             modelMap[model.cid] = true;
    153             set.push(model);
    154           }
    155         }
    156       }
    157 
    158       // Remove stale models.
    159       if (remove) {
    160         for (i = 0; i < this.length; i++) {
    161           model = this.models[i];
    162           if (!modelMap[model.cid]) toRemove.push(model);
    163         }
    164         if (toRemove.length) this._removeModels(toRemove, options);
    165       }
    166 
    167       // See if sorting is needed, update `length` and splice in new models.
    168       var orderChanged = false;
    169       var replace = !sortable && add && remove;
    170       if (set.length && replace) {
    171         orderChanged = this.length !== set.length || _.some(this.models, function(m, index) {
    172           return m !== set[index];
    173         });
    174         this.models.length = 0;
    175         splice(this.models, set, 0);
    176         this.length = this.models.length;
    177       } else if (toAdd.length) {
    178         if (sortable) sort = true;
    179         splice(this.models, toAdd, at == null ? this.length : at);
    180         this.length = this.models.length;
    181       }
    182 
    183       // Silently sort the collection if appropriate.
    184       if (sort) this.sort({silent: true});
    185 
    186       // Unless silenced, it's time to fire all appropriate add/sort/update events.
    187       if (!options.silent) {
    188         for (i = 0; i < toAdd.length; i++) {
    189           if (at != null) options.index = at + i;
    190           model = toAdd[i];
    191           model.trigger('add', model, this, options);
    192         }
    193         if (sort || orderChanged) this.trigger('sort', this, options);
    194         if (toAdd.length || toRemove.length || toMerge.length) {
    195           options.changes = {
    196             added: toAdd,
    197             removed: toRemove,
    198             merged: toMerge
    199           };
    200           this.trigger('update', this, options);
    201         }
    202       }
    203 
    204       // Return the added (or merged) model (or models).
    205       return singular ? models[0] : models;
    206     },
    207 
    208     // When you have more items than you want to add or remove individually,
    209     // you can reset the entire set with a new list of models, without firing
    210     // any granular `add` or `remove` events. Fires `reset` when finished.
    211     // Useful for bulk operations and optimizations.
    212     reset: function(models, options) {
    213       options = options ? _.clone(options) : {};
    214       for (var i = 0; i < this.models.length; i++) {
    215         this._removeReference(this.models[i], options);
    216       }
    217       options.previousModels = this.models;
    218       this._reset();
    219       models = this.add(models, _.extend({silent: true}, options));
    220       if (!options.silent) this.trigger('reset', this, options);
    221       return models;
    222     },
    223 
    224     // Add a model to the end of the collection.
    225     push: function(model, options) {
    226       return this.add(model, _.extend({at: this.length}, options));
    227     },
    228 
    229     // Remove a model from the end of the collection.
    230     pop: function(options) {
    231       var model = this.at(this.length - 1);
    232       return this.remove(model, options);
    233     },
    234 
    235     // Add a model to the beginning of the collection.
    236     unshift: function(model, options) {
    237       return this.add(model, _.extend({at: 0}, options));
    238     },
    239 
    240     // Remove a model from the beginning of the collection.
    241     shift: function(options) {
    242       var model = this.at(0);
    243       return this.remove(model, options);
    244     },
    245 
    246     // Slice out a sub-array of models from the collection.
    247     slice: function() {
    248       return slice.apply(this.models, arguments);
    249     },
    250 
    251     // Get a model from the set by id, cid, model object with id or cid
    252     // properties, or an attributes object that is transformed through modelId.
    253     get: function(obj) {
    254       if (obj == null) return void 0;
    255       return this._byId[obj] ||
    256         this._byId[this.modelId(obj.attributes || obj)] ||
    257         obj.cid && this._byId[obj.cid];
    258     },
    259 
    260     // Returns `true` if the model is in the collection.
    261     has: function(obj) {
    262       return this.get(obj) != null;
    263     },
    264 
    265     // Get the model at the given index.
    266     at: function(index) {
    267       if (index < 0) index += this.length;
    268       return this.models[index];
    269     },
    270 
    271     // Return models with matching attributes. Useful for simple cases of
    272     // `filter`.
    273     where: function(attrs, first) {
    274       return this[first ? 'find' : 'filter'](attrs);
    275     },
    276 
    277     // Return the first model with matching attributes. Useful for simple cases
    278     // of `find`.
    279     findWhere: function(attrs) {
    280       return this.where(attrs, true);
    281     },
    282 
    283     // Force the collection to re-sort itself. You don't need to call this under
    284     // normal circumstances, as the set will maintain sort order as each item
    285     // is added.
    286     sort: function(options) {
    287       var comparator = this.comparator;
    288       if (!comparator) throw new Error('Cannot sort a set without a comparator');
    289       options || (options = {});
    290 
    291       var length = comparator.length;
    292       if (_.isFunction(comparator)) comparator = _.bind(comparator, this);
    293 
    294       // Run sort based on type of `comparator`.
    295       if (length === 1 || _.isString(comparator)) {
    296         this.models = this.sortBy(comparator);
    297       } else {
    298         this.models.sort(comparator);
    299       }
    300       if (!options.silent) this.trigger('sort', this, options);
    301       return this;
    302     },
    303 
    304     // Pluck an attribute from each model in the collection.
    305     pluck: function(attr) {
    306       return this.map(attr + '');
    307     },
    308 
    309     // Fetch the default set of models for this collection, resetting the
    310     // collection when they arrive. If `reset: true` is passed, the response
    311     // data will be passed through the `reset` method instead of `set`.
    312     fetch: function(options) {
    313       options = _.extend({parse: true}, options);
    314       var success = options.success;
    315       var collection = this;
    316       options.success = function(resp) {
    317         var method = options.reset ? 'reset' : 'set';
    318         collection[method](resp, options);
    319         if (success) success.call(options.context, collection, resp, options);
    320         collection.trigger('sync', collection, resp, options);
    321       };
    322       wrapError(this, options);
    323       return this.sync('read', this, options);
    324     },
    325 
    326     // Create a new instance of a model in this collection. Add the model to the
    327     // collection immediately, unless `wait: true` is passed, in which case we
    328     // wait for the server to agree.
    329     create: function(model, options) {
    330       options = options ? _.clone(options) : {};
    331       var wait = options.wait;
    332       model = this._prepareModel(model, options);
    333       if (!model) return false;
    334       if (!wait) this.add(model, options);
    335       var collection = this;
    336       var success = options.success;
    337       options.success = function(m, resp, callbackOpts) {
    338         if (wait) collection.add(m, callbackOpts);
    339         if (success) success.call(callbackOpts.context, m, resp, callbackOpts);
    340       };
    341       model.save(null, options);
    342       return model;
    343     },
    344 
    345     // **parse** converts a response into a list of models to be added to the
    346     // collection. The default implementation is just to pass it through.
    347     //可以复写该方法
    348     parse: function(resp, options) {
    349       return resp;
    350     },
    351 
    352     // Create a new collection with an identical list of models as this one.
    353     clone: function() {
    354       return new this.constructor(this.models, {
    355         model: this.model,
    356         comparator: this.comparator
    357       });
    358     },
    359 
    360     // Define how to uniquely identify models in the collection.
    361     modelId: function(attrs) {
    362       return attrs[this.model.prototype.idAttribute || 'id'];
    363     },
    364 
    365     // Private method to reset all internal state. Called when the collection
    366     // is first initialized or reset.
    367     _reset: function() {
    368       this.length = 0;
    369       this.models = [];
    370       this._byId  = {};
    371     },
    372 
    373     // Prepare a hash of attributes (or other model) to be added to this
    374     // collection.
    375     _prepareModel: function(attrs, options) {
    376       if (this._isModel(attrs)) {
    377         if (!attrs.collection) attrs.collection = this;
    378         return attrs;
    379       }
    380       options = options ? _.clone(options) : {};
    381       options.collection = this;
    382       var model = new this.model(attrs, options);
    383       if (!model.validationError) return model;
    384       this.trigger('invalid', this, model.validationError, options);
    385       return false;
    386     },
    387 
    388     // Internal method called by both remove and set.
    389     _removeModels: function(models, options) {
    390       var removed = [];
    391       for (var i = 0; i < models.length; i++) {
    392         var model = this.get(models[i]);
    393         if (!model) continue;
    394 
    395         var index = this.indexOf(model);
    396         this.models.splice(index, 1);
    397         this.length--;
    398 
    399         // Remove references before triggering 'remove' event to prevent an
    400         // infinite loop. #3693
    401         delete this._byId[model.cid];
    402         var id = this.modelId(model.attributes);
    403         if (id != null) delete this._byId[id];
    404 
    405         if (!options.silent) {
    406           options.index = index;
    407           model.trigger('remove', model, this, options);
    408         }
    409 
    410         removed.push(model);
    411         this._removeReference(model, options);
    412       }
    413       return removed;
    414     },
    415 
    416     // Method for checking whether an object should be considered a model for
    417     // the purposes of adding to the collection.
    418     _isModel: function(model) {
    419       return model instanceof Model;
    420     },
    421 
    422     // Internal method to create a model's ties to a collection.
    423     _addReference: function(model, options) {
    424       this._byId[model.cid] = model;
    425       var id = this.modelId(model.attributes);
    426       if (id != null) this._byId[id] = model;
    427       model.on('all', this._onModelEvent, this);
    428     },
    429 
    430     // Internal method to sever a model's ties to a collection.
    431     _removeReference: function(model, options) {
    432       delete this._byId[model.cid];
    433       var id = this.modelId(model.attributes);
    434       if (id != null) delete this._byId[id];
    435       if (this === model.collection) delete model.collection;
    436       model.off('all', this._onModelEvent, this);
    437     },
    438 
    439     // Internal method called every time a model in the set fires an event.
    440     // Sets need to update their indexes when models change ids. All other
    441     // events simply proxy through. "add" and "remove" events that originate
    442     // in other collections are ignored.
    443     _onModelEvent: function(event, model, collection, options) {
    444       if (model) {
    445         if ((event === 'add' || event === 'remove') && collection !== this) return;
    446         if (event === 'destroy') this.remove(model, options);
    447         if (event === 'change') {
    448           var prevId = this.modelId(model.previousAttributes());
    449           var id = this.modelId(model.attributes);
    450           if (prevId !== id) {
    451             if (prevId != null) delete this._byId[prevId];
    452             if (id != null) this._byId[id] = model;
    453           }
    454         }
    455       }
    456       this.trigger.apply(this, arguments);
    457     }
    458 
    459   });
    460 
    461   // Underscore methods that we want to implement on the Collection.
    462   // 90% of the core usefulness of Backbone Collections is actually implemented
    463   // right here:
    464   var collectionMethods = {forEach: 3, each: 3, map: 3, collect: 3, reduce: 0,
    465       foldl: 0, inject: 0, reduceRight: 0, foldr: 0, find: 3, detect: 3, filter: 3,
    466       select: 3, reject: 3, every: 3, all: 3, some: 3, any: 3, include: 3, includes: 3,
    467       contains: 3, invoke: 0, max: 3, min: 3, toArray: 1, size: 1, first: 3,
    468       head: 3, take: 3, initial: 3, rest: 3, tail: 3, drop: 3, last: 3,
    469       without: 0, difference: 0, indexOf: 3, shuffle: 1, lastIndexOf: 3,
    470       isEmpty: 1, chain: 1, sample: 3, partition: 3, groupBy: 3, countBy: 3,
    471       sortBy: 3, indexBy: 3, findIndex: 3, findLastIndex: 3};
    472 
    473   // Mix in each Underscore method as a proxy to `Collection#models`.
    474   addUnderscoreMethods(Collection, collectionMethods, 'models');
  • 相关阅读:
    TeeChart的X轴,使用伪装的时间
    线程池
    修练8年C++面向对象程序设计之体会
    使用RESTClient插件数据模拟(GET,POST)提交
    :施密特建议尾随年轻的专业人士了解技术公司
    社会保障系列1《介绍》
    Centos根据系统VPS安装SendMail组件使WordPress支持E-mail
    Codeforces Round #107 (Div. 2)---A. Soft Drinking
    [Unity3D]Unity3D游戏开发3D选择场景中的对象,并显示轮廓效果强化版
    怎么样ubuntu 64 11.04 在执行32位程序
  • 原文地址:https://www.cnblogs.com/wangwei1314/p/5595864.html
Copyright © 2011-2022 走看看