Skip to content

Commit

Permalink
waitfor array
Browse files Browse the repository at this point in the history
  • Loading branch information
RickButler committed Oct 13, 2018
1 parent 283b599 commit a55462b
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 30 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 they "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])`
Expand Down
46 changes: 24 additions & 22 deletions ampersand-view.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -75,7 +73,7 @@ var BaseState = State.extend({
},
rendered: {
deps: ['_rendered'],
fn: function() {
fn: function () {
if (this._rendered) {
this.trigger('render', this);
return true;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -215,7 +213,7 @@ assign(View.prototype, {
},

// ## renderSubview
// Pass it a view instance and a container element
// Pass it a view instance and a container elementnpm star
// to render it in. It's `remove` method will be called
// when the parent view is destroyed.
renderSubview: function (view, container) {
Expand Down Expand Up @@ -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);
Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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();
Expand All @@ -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);
Expand Down
23 changes: 16 additions & 7 deletions test/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -864,10 +864,11 @@ test('make sure subviews dont fire until their `waitFor` is done', function (t)
});

var View = AmpersandView.extend({
template: '<div><span class="container"></span><span data-hook="sub"></span></div>',
template: '<div><span class="container"></span><span data-hook="sub"></span><span data-hook="subthree"></span></div>',
autoRender: true,
props: {
model2: 'state'
model2: 'state',
subThreeReady: 'boolean'
},
subviews: {
sub1: {
Expand All @@ -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, '<div><span class="container"></span><span data-hook="sub"></span></div>');
t.equal(view._events.change.length, 3, 'three change events change events on instantiation');
t.equal(view.el.outerHTML, '<div><span class="container"></span><span data-hook="sub"></span><span data-hook="subthree"></span></div>', 'the subviews should be empty');
view.model = new Model();
t.equal(view._events.change.length, 1);
t.equal(view.el.outerHTML, '<div><span class="container"><span>yes</span></span><span data-hook="sub"></span></div>');
t.equal(view._events.change.length, 2, 'triggers change');
t.equal(view.el.outerHTML, '<div><span class="container"><span>yes</span></span><span data-hook="sub"></span><span data-hook="subthree"></span></div>', 'the subview sub1 should have rendered');
view.model2 = new Model();
t.equal(view.el.outerHTML, '<div><span class="container"><span>yes</span></span><span data-hook="sub"><span>yes</span></span></div>');
t.equal(view.el.outerHTML, '<div><span class="container"><span>yes</span></span><span data-hook="sub"><span>yes</span></span><span data-hook="subthree"></span></div>','the subviews sub2 should have rendered');
view.subThreeReady = true;
t.equal(view.el.outerHTML, '<div><span class="container"><span>yes</span></span><span data-hook="sub"><span>yes</span></span><span data-hook="subthree"><span>yes</span></span></div>','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 () {
Expand Down

0 comments on commit a55462b

Please sign in to comment.