diff --git a/src/ns.model.js b/src/ns.model.js index 7aab2f93..c177808b 100644 --- a/src/ns.model.js +++ b/src/ns.model.js @@ -451,7 +451,8 @@ ns.Model.prototype.getRequestParams = function() { * @returns {ns.Model} */ ns.Model.get = function(id, params) { - var model = ns.Model.find(id, params); + + var model = this._find(id, params); if (!model) { var Ctor = _ctors[id]; @@ -467,19 +468,33 @@ ns.Model.get = function(id, params) { return model; }; +/** + * Returns valid cached model instance. + * @param {String} id Model's ID. + * @param {Object} [params] Model's params + * @returns {ns.Model|null} + */ +ns.Model.getValid = function(id, params) { + var model = this._find(id, params); + if (model && model.isValid()) { + return model; + } + return null; +}; + /** * Returns cached model instance. * @param {String} id Model's ID. * @param {Object} [params] Model's params - * @returns {ns.Model|undefined} + * @returns {ns.Model|null} */ -ns.Model.find = function(id, params) { +ns.Model._find = function(id, params) { if (!(id in _infos)) { throw new Error('[ns.Model] "' + id + '" is not defined'); } var key = ns.Model.key(id, params); - return _cache[id][key]; + return _cache[id][key] || null; }; /** @@ -496,9 +511,6 @@ ns.Model.destroy = function(model) { var cached = _cache[id][key]; if (cached) { - // remove from cache - delete _cache[id][key]; - // notify subscribers about disappearance model.trigger('ns-model-destroyed'); @@ -508,8 +520,8 @@ ns.Model.destroy = function(model) { }; // Проверяем, есть ли модель в кэше и валидна ли она. -ns.Model.isValid = function(id, key) { - var model = ns.Model.get(id, key); +ns.Model.isValid = function(id, params) { + var model = ns.Model.get(id, params); if (!model) { return; } // undefined означает, что кэша нет вообще, а false -- что он инвалидный. return model.isValid(); diff --git a/src/ns.view.js b/src/ns.view.js index 7f91fdc0..c6f48fa1 100644 --- a/src/ns.view.js +++ b/src/ns.view.js @@ -97,17 +97,16 @@ ns.View.prototype._init = function(id, params, async) { * Инициализирует модели */ ns.View.prototype._initModels = function() { - // Создаёи модели или берем их из кэша, если они уже есть - for (var model_id in this.info.models) { - this._initModel(model_id); - } -}; - -ns.View.prototype._initModel = function(id) { if (!this.models) { this.models = {}; } - this.models[id] = ns.Model.get(id, this.params); + + // Создаём модели или берем их из кэша, если они уже есть + for (var id in this.info.models) { + if (!this.models[id]) { + this.models[id] = ns.Model.get(id, this.params); + } + } }; // --------------------------------------------------------------------------------------------------------------- // @@ -653,8 +652,7 @@ ns.View.prototype._bindModels = function() { var model = models[model_id]; model.on('ns-model-destroyed', function() { - delete that.models[this.id]; - that._initModel(this.id); + that.invalidate(); }); var jpaths = subviews[model_id]; @@ -909,7 +907,7 @@ ns.View.prototype.isSubviewsOk = function() { * Возвращает true, если блок валиден. * @return {Boolean} */ -ns.View.prototype.isValid = function() { +ns.View.prototype.isValid = ns.View.prototype.isValidSelf = function() { return this.isOk() && this.isSubviewsOk() && this.isModelsValidWithVersions(); }; @@ -974,6 +972,32 @@ ns.View.prototype._apply = function(callback) { * @return {*} */ ns.View.prototype._getRequestViews = function(updated, pageLayout, params) { + + // При необходимости добавим текущий вид в список "запрашиваемых" + this._tryPushToRequest(updated); + + // Если views еще не определены (первая отрисовка) + if (!this.views) { + // FIXME: Почему бы это в конструкторе не делать? + this.views = {}; + // Создаем подблоки + for (var view_id in pageLayout) { + this._addView(view_id, params, pageLayout[view_id].type); + } + } + + this._apply(function(view, id) { + view._getRequestViews(updated, pageLayout[id].views, params); + }); + + return updated; +}; + +/** + * Добавляет вид в соответствующий список "запрашиваемых" видов в случае, + * если запрос необходим + */ +ns.View.prototype._tryPushToRequest = function(updated) { /** * Флаг, означающий, что view грузится асинхронно. * @type {Boolean} @@ -994,25 +1018,11 @@ ns.View.prototype._getRequestViews = function(updated, pageLayout, params) { // прекращаем обработку return updated; } - } else if (!this.isValid()) { + } else if (!this.isValidSelf()) { // если обычный блок не валиден updated.sync.push(this); } - // Если views еще не определены (первая отрисовка) - if (!this.views) { - // FIXME: Почему бы это в конструкторе не делать? - this.views = {}; - // Создаем подблоки - for (var view_id in pageLayout) { - this._addView(view_id, params, pageLayout[view_id].type); - } - } - - this._apply(function(view, id) { - view._getRequestViews(updated, pageLayout[id].views, params); - }); - return updated; }; diff --git a/src/ns.viewCollection.js b/src/ns.viewCollection.js index fd4d19aa..b3a6f5da 100644 --- a/src/ns.viewCollection.js +++ b/src/ns.viewCollection.js @@ -58,8 +58,7 @@ ns.ViewCollection.prototype._bindModels = function() { var model = models[model_id]; model.on('ns-model-destroyed', function() { - delete that.models[this.id]; - that._initModel(this.id); + that.invalidate(); }); model.on('ns-model-changed', function(e, o) { @@ -75,12 +74,6 @@ ns.ViewCollection.prototype.isValid = function() { return this.isValidSelf() && this.isValidDesc(); }; -/** - * Check self validity (except items). - * @returns {Boolean} - */ -ns.ViewCollection.prototype.isValidSelf = ns.View.prototype.isValid; - ns.ViewCollection.prototype.isValidDesc = function() { for (var key in this.views) { if (!this.views[key].isValid()) { @@ -159,34 +152,7 @@ ns.ViewCollection.prototype._apply = function(callback) { } }; -ns.ViewCollection.prototype._getRequestViews = function(updated) { - /** - * Флаг, означающий, что view грузится асинхронно. - * @type {Boolean} - */ - this.asyncState = false; - - if (this.async) { - var hasValidModels = this.isModelsValid(); - var hasValidStatus = this.isOk(); - if (hasValidModels && !hasValidStatus) { - // если асинхронный блок имеет валидные модели, но невалидный статус - рисуем его синхронно - updated.sync.push(this); - - } else if (!hasValidModels) { - this.asyncState = true; - // если асинхронный блок имеет невалидные модели, то его не надо рисовать - updated.async.push(this); - // прекращаем обработку - return updated; - } - } else if (!this.isValidSelf()) { - // если обычный блок не валиден - updated.sync.push(this); - } - - return updated; -}; +ns.ViewCollection.prototype._getRequestViews = ns.View.prototype._tryPushToRequest; ns.ViewCollection.prototype._getUpdateTree = function(tree, layout, params) { var decl; diff --git a/test/spec/ns.box.js b/test/spec/ns.box.js index f5887a6e..e2c5948e 100644 --- a/test/spec/ns.box.js +++ b/test/spec/ns.box.js @@ -68,7 +68,7 @@ describe('ns.Box', function() { }, rewriteParamsOnInit: function(params) { return { - pOwn: ns.Model.find('model4').get('.value') + pOwn: ns.Model.getValid('model4').get('.value') }; } }); diff --git a/test/spec/ns.model.js b/test/spec/ns.model.js index a474a3de..b8abfe1e 100644 --- a/test/spec/ns.model.js +++ b/test/spec/ns.model.js @@ -136,19 +136,20 @@ describe('ns.Model', function() { }); }); - describe('ns.Model.find():', function() { + describe('ns.Model.getValid():', function() { - it('should return undefined if model doesn\'t exists', function() { - expect(ns.Model.find('m1')).to.be(undefined); + it('should return null if model doesn\'t exists', function() { + expect(ns.Model.getValid('m1')).to.be(null); }); - it('should return model if exists', function() { + it('should return valid model if exists', function() { var m = ns.Model.get('m1'); - expect(ns.Model.find('m1')).to.be.ok(m); + m.setData({foo: 'bar'}); + expect(ns.Model.getValid('m1')).to.be.ok(m); }); it('should throw exception if model is not defined', function() { - var exists = function() { ns.Model.find('non-exists-model'); }; + var exists = function() { ns.Model.getValid('non-exists-model'); }; expect(exists).to.throwException(); }); @@ -166,7 +167,7 @@ describe('ns.Model', function() { }); it('should throw exception if model is not defined', function() { - var exists = function() { ns.Model.find('non-exists-model'); }; + var exists = function() { ns.Model.getValid('non-exists-model'); }; expect(exists).to.throwException(); }); @@ -551,7 +552,7 @@ describe('ns.Model', function() { this.model2.destroyWith(this.model1); ns.Model.destroy(this.model1); - expect(ns.Model.find('model2', { id: 1 })).not.to.be.ok(); + expect(ns.Model.getValid('model2', { id: 1 })).not.to.be.ok(); }); it('should throw error if tried to destroy ns.Model with string', function() { @@ -559,7 +560,7 @@ describe('ns.Model', function() { }); it('should throw error if tried to destroy ns.Model with undefined', function() { - expect(function() { this.model1.destroyWith(ns.Model.find('model2', {id: 2})); }).to.throwException(); + expect(function() { this.model1.destroyWith(ns.Model.getValid('model2', {id: 2})); }).to.throwException(); }); }); diff --git a/test/spec/ns.viewCollection.js b/test/spec/ns.viewCollection.js index d5111e42..3079e7e9 100644 --- a/test/spec/ns.viewCollection.js +++ b/test/spec/ns.viewCollection.js @@ -437,8 +437,10 @@ describe('ns.ViewCollection', function() { .start() .done(function() { ns.Model.destroy(ns.Model.get('mCollection')); + ns.Model.destroy(ns.Model.get('mItem', {id: '0'})); + ns.Model.destroy(ns.Model.get('mItem', {id: '1'})); - ns.Model.get('mCollection').setData({data: [{id: 3}]}); + ns.Model.get('mCollection').setData({data: [{id: '2'}]}); new ns.Update(this.APP, layout, {}) .start() @@ -452,6 +454,11 @@ describe('ns.ViewCollection', function() { delete this.APP; }); + it('shouldn`t find destroyed models', function() { + expect(ns.Model.getValid('mItem', {id: '0'})).not.to.be.ok(); + expect(ns.Model.getValid('mItem', {id: '1'})).not.to.be.ok(); + }); + it('should have 1 node for view vItem', function() { expect(this.APP.node.querySelectorAll('.ns-view-vItem').length).to.be(1); });