reactively determined Component class

This commit is contained in:
David Greenspan
2013-07-08 11:21:06 -07:00
parent 4ec6f22683
commit 18cd889c54
5 changed files with 150 additions and 22 deletions

View File

@@ -46,4 +46,43 @@ Template.item({
// $(table).append(frag);
//
// console.log(TABLE.rows.length);
//});
//});
Span = UIComponent.extend({
render: function (buf) {
buf("<span style='background:red;margin:5px'>Hello</span>");
}
});
Div = UIComponent.extend({
render: function (buf) {
buf("<div style='background:blue;margin:5px'>World</div>");
}
});
Either = UIComponent.extend({
render: function (buf) {
buf(Div.create(),
{
type: function () { return window[Session.get('which')]; },
args: {
built: function () {
var self = this;
self.$("*").on('click', function (evt) {
Session.set(
'which',
Session.get('which') === 'Div' ? 'Span' : 'Div');
});
}
}
},
{ type: Span });
}
});
Meteor.startup(function () {
Session.set('which', 'Span');
var x = Either.create({isRoot: true});
x.attach(document.body);
});

View File

@@ -94,33 +94,16 @@ Component({
_populate: function (div) {
var self = this;
var strs = [];
var randomString = Random.id();
var commentUid = 1;
var componentsToAttach = {};
var buf = makeRenderBuffer(self);
self.render(buf);
self.render(function (/*args*/) {
for (var i = 0; i < arguments.length; i++) {
var arg = arguments[i];
if (typeof arg === 'string') {
strs.push(arg);
} else if (arg instanceof Component) {
var commentString = randomString + '_' + (commentUid++);
strs.push('<!--', commentString, '-->');
self.add(arg);
componentsToAttach[commentString] = arg;
} else {
throw new Error("Expected string or Component");
}
}
});
var html = strs.join('');
var html = buf.getHtml();
$(div).append(html);
var start = div.firstChild;
var end = div.lastChild;
var componentsToAttach = buf.componentsToAttach;
// walk div and replace comments with Components
var wireUpDOM = function (parent) {
@@ -182,6 +165,7 @@ Component({
if (c.firstRun) {
var div = makeSafeDiv();
// capture reactivity:
var info = self._populate(div);
if (! div.firstChild)
@@ -191,6 +175,7 @@ Component({
self.start = info.start || div.firstChild;
self.end = info.end || div.lastChild;
} else {
// capture reactivity:
self._rebuild(c.builtChildren);
}
@@ -593,6 +578,38 @@ Component({
self._computations.push(c);
return c;
},
replaceChild: function (oldChild, newChild) {
var self = this;
self._requireBuilt();
oldChild._requireBuilt();
if (! oldChild.isAttached)
throw new Error("Child to replace must be attached");
var lastNode = oldChild.lastNode();
var parentNode = lastNode.parentNode;
var nextNode = lastNode.nextSibling;
oldChild.remove();
self.insertBefore(newChild, nextNode, parentNode);
},
swapChild: function (oldChild, newChild) {
var self = this;
self._requireBuilt();
oldChild._requireBuilt();
if (! oldChild.isAttached)
throw new Error("Child to swap out must be attached");
var lastNode = oldChild.lastNode();
var parentNode = lastNode.parentNode;
var nextNode = lastNode.nextSibling;
oldChild.detach();
self.insertBefore(newChild, nextNode, parentNode);
}
// If Component is ever emptied, it gets an empty comment node.

View File

@@ -16,6 +16,7 @@ Package.on_use(function (api) {
api.add_files(['base.js',
'lifecycle.js',
'tree.js',
'render.js',
'dom.js',
'forms.js',
'components.js',

65
packages/ui/render.js Normal file
View File

@@ -0,0 +1,65 @@
var Component = UIComponent;
makeRenderBuffer = function (component, options) {
var isPreview = !! options && options.preview;
var strs = [];
var componentsToAttach = {};
var randomString = Random.id();
var commentUid = 1;
var handle = function (arg) {
if (typeof arg === 'string') {
strs.push(arg);
} else if (arg instanceof Component) {
var commentString = randomString + '_' + (commentUid++);
strs.push('<!--', commentString, '-->');
component.add(arg);
componentsToAttach[commentString] = arg;
} else if (arg.type) {
// `{type: componentTypeOrFunction, args: object}`
if (Component.isType(arg.type)) {
handle(arg.type.create(arg.args));
} else if (typeof arg.type === 'function') {
var curType;
component.autorun(function (c) {
// capture reactivity:
var type = arg.type();
if (c.firstRun) {
curType = type;
} else if (component.stage !== Component.BUILT ||
! component.hasChild(curChild)) {
c.stop();
} else if (type !== curType) {
var oldChild = curChild;
curType = type;
Deps.nonreactive(function () {
curChild = curType.create(arg.args);
component.replaceChild(oldChild, curChild);
});
}
});
var curChild = curType.create(arg.args);
handle(curChild);
} else {
throw new Error("Expected 'type' to be Component or function");
}
} else {
throw new Error("Expected string or Component");
}
};
var buf = function (/*args*/) {
for (var i = 0; i < arguments.length; i++)
handle(arguments[i]);
};
buf.getHtml = function () {
return strs.join('');
};
buf.componentsToAttach = componentsToAttach;
return buf;
};

View File

@@ -132,6 +132,12 @@ Component({
self._added();
},
hasChild: function (comp) {
this._requireNotDestroyed();
return this.children[comp.guid] === comp;
},
extendHooks: {
isRoot: function (value) {
if (value)