From 9322ff37a02898ee79fa70d8c00ca764537464f1 Mon Sep 17 00:00:00 2001 From: RickButler Date: Sat, 13 Oct 2018 10:33:31 -0700 Subject: [PATCH] waitFor array --- README.md | 2 +- ampersand-view.js | 44 +++++++++++++++++++++++--------------------- test/main.js | 23 ++++++++++++++++------- 3 files changed, 40 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 045cf8b..187dd07 100644 --- a/README.md +++ b/README.md @@ -580,7 +580,7 @@ subview declarations consist of: * selector {String} Selector that describes the element within the view that should hold the subview. * hook {String} Alternate method for specifying a container element using its `data-hook` attribute. Equivalent to `selector: '[data-hook=some-hook]'`. * constructor {ViewConstructor} Any [view conventions compliant](http://ampersandjs.com/learn/view-conventions) view constructor. It will be initialized with `{el: [Element grabbed from selector], parent: [reference to parent view instance]}`. So if you don't need to do any custom setup, you can just provide the constructor. -* waitFor {String} String specifying they "key-path" (i.e. 'model.property') of the view that must be "truthy" before it should consider the subview ready. +* waitFor {String|Array} String or Array of Strings specifying the "key-path" (i.e. 'model.property') of the view that must be "truthy" before it should consider the subview ready. * prepareView {Function} Function that will be called once any `waitFor` condition is met. It will be called with the `this` context of the parent view and with the element that matches the selector as the argument. It should return an instantiated view instance. ### delegateEvents `view.delegateEvents([events])` diff --git a/ampersand-view.js b/ampersand-view.js index e925031..b15ec1a 100644 --- a/ampersand-view.js +++ b/ampersand-view.js @@ -3,26 +3,24 @@ var State = require('ampersand-state'); var CollectionView = require('ampersand-collection-view'); var domify = require('domify'); var uniqueId = require("lodash/uniqueId"); -var pick = require("lodash/pick"); var assign = require("lodash/assign"); -var forEach = require("lodash/forEach"); var result = require("lodash/result"); var last = require("lodash/last"); var isString = require("lodash/isString"); -var bind = require("lodash/bind"); var flatten = require("lodash/flatten"); var invokeMap = require("lodash/invokeMap"); var events = require('events-mixin'); var matches = require('matches-selector'); var bindings = require('ampersand-dom-bindings'); var getPath = require('lodash/get'); +var castArray = require('lodash/castArray'); function View(attrs) { this.cid = uniqueId('view'); attrs || (attrs = {}); var parent = attrs.parent; delete attrs.parent; - BaseState.call(this, attrs, {init: false, parent: parent}); + BaseState.call(this, attrs, { init: false, parent: parent }); this.on('change:el', this._handleElementChange, this); this._upsertBindings(); this.template = attrs.template || this.template; @@ -75,7 +73,7 @@ var BaseState = State.extend({ }, rendered: { deps: ['_rendered'], - fn: function() { + fn: function () { if (this._rendered) { this.trigger('render', this); return true; @@ -141,7 +139,7 @@ assign(View.prototype, { // Initialize is an empty function by default. Override it with your own // initialization logic. - initialize: function () {}, + initialize: function () { }, // **render** is the core function that your view can override. Its job is // to populate its element (`this.el`), with the appropriate HTML. @@ -267,12 +265,16 @@ assign(View.prototype, { subview.selector = subview.container; } var opts = this._parseSubviewOpts(subview); + var hasViewPath = function (path) { + return !!getPath(this, path); + }; + function action() { var el, subview; // if not rendered or we can't find our element, stop here. if (!this.el || !(el = this.query(opts.selector))) return; - if (!opts.waitFor || getPath(this, opts.waitFor)) { + if (!opts.waitFor.length || opts.waitFor.every(hasViewPath, this)) { subview = this[name] = opts.prepareView.call(this, el); if (!subview.el) { this.renderSubview(subview, el); @@ -299,7 +301,7 @@ assign(View.prototype, { var self = this; var opts = { selector: subview.selector || '[data-hook="' + subview.hook + '"]', - waitFor: subview.waitFor || '', + waitFor: subview.waitFor ? castArray(subview.waitFor) : [], prepareView: subview.prepareView || function () { return new subview.constructor({ parent: self @@ -383,13 +385,13 @@ assign(View.prototype, { return this.registerSubview(collectionView); }, - _setRender: function(obj) { + _setRender: function (obj) { Object.defineProperty(obj, 'render', { - get: function() { + get: function () { return this._render; }, - set: function(fn) { - this._render = function() { + set: function (fn) { + this._render = function () { this._upsertBindings(); fn.apply(this, arguments); this._rendered = true; @@ -399,13 +401,13 @@ assign(View.prototype, { }); }, - _setRemove: function(obj) { + _setRemove: function (obj) { Object.defineProperty(obj, 'remove', { - get: function() { + get: function () { return this._remove; }, - set: function(fn) { - this._remove = function() { + set: function (fn) { + this._remove = function () { fn.apply(this, arguments); this._rendered = false; this._downsertBindings(); @@ -415,18 +417,18 @@ assign(View.prototype, { }); }, - _downsertBindings: function() { + _downsertBindings: function () { var parsedBindings = this._parsedBindings; if (!this.bindingsSet) return; - if (this._subviews){ - invokeMap(flatten(this._subviews), 'remove'); - this._subviews = []; + if (this._subviews) { + invokeMap(flatten(this._subviews), 'remove'); + this._subviews = []; } this.stopListening(); this.bindingsSet = false; }, - _upsertBindings: function(attrs) { + _upsertBindings: function (attrs) { attrs = attrs || this; if (this.bindingsSet) return; this._parsedBindings = bindings(this.bindings, this); diff --git a/test/main.js b/test/main.js index 5c895e5..8ec9ec7 100644 --- a/test/main.js +++ b/test/main.js @@ -864,10 +864,11 @@ test('make sure subviews dont fire until their `waitFor` is done', function (t) }); var View = AmpersandView.extend({ - template: '
', + template: '
', autoRender: true, props: { - model2: 'state' + model2: 'state', + subThreeReady: 'boolean' }, subviews: { sub1: { @@ -879,22 +880,30 @@ test('make sure subviews dont fire until their `waitFor` is done', function (t) waitFor: 'model2', hook: 'sub', constructor: Sub + }, + sub3: { + waitFor: ['model', 'model2','subThreeReady'], + hook: 'subthree', + constructor: Sub } } }); var view = new View(); - t.equal(view._events.change.length, 2); - t.equal(view.el.outerHTML, '
'); + t.equal(view._events.change.length, 3, 'three change events change events on instantiation'); + t.equal(view.el.outerHTML, '
', 'the subviews should be empty'); view.model = new Model(); - t.equal(view._events.change.length, 1); - t.equal(view.el.outerHTML, '
yes
'); + t.equal(view._events.change.length, 2, 'triggers change'); + t.equal(view.el.outerHTML, '
yes
', 'the subview sub1 should have rendered'); view.model2 = new Model(); - t.equal(view.el.outerHTML, '
yesyes
'); + t.equal(view.el.outerHTML, '
yesyes
','the subviews sub2 should have rendered'); + view.subThreeReady = true; + t.equal(view.el.outerHTML, '
yesyesyes
','the subviews sub3 should have rendered'); t.notOk(view._events.change); t.end(); }); + test('make sure template can return a dom node', function (t) { var Sub = AmpersandView.extend({ template: function () {