mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
making block helpers work
This commit is contained in:
@@ -1384,6 +1384,14 @@ var optimize = function (tree) {
|
||||
return optTree;
|
||||
};
|
||||
|
||||
var builtInComponents = {
|
||||
'content': '__content',
|
||||
'elseContent': '__elseContent',
|
||||
'if': 'UI.If2',
|
||||
'unless': 'UI.Unless2',
|
||||
'with': 'UI.With2'
|
||||
};
|
||||
|
||||
var replaceSpecials = function (node) {
|
||||
if (UI.isComponent(node)) {
|
||||
return node;
|
||||
@@ -1416,8 +1424,15 @@ var replaceSpecials = function (node) {
|
||||
var path = tag.path;
|
||||
var compCode = codeGenPath2(path);
|
||||
|
||||
if (path.length === 1)
|
||||
compCode = '(Template[' + toJSLiteral(path[0]) + '] || ' + compCode + ')';
|
||||
if (path.length === 1) {
|
||||
var compName = path[0];
|
||||
if (builtInComponents.hasOwnProperty(compName)) {
|
||||
compCode = builtInComponents[compName];
|
||||
} else {
|
||||
compCode = ('(Template[' + toJSLiteral(path[0]) +
|
||||
'] || ' + compCode + ')');
|
||||
}
|
||||
}
|
||||
|
||||
var includeArgs = codeGenInclusionArgs(tag);
|
||||
|
||||
@@ -1437,6 +1452,17 @@ var replaceSpecials = function (node) {
|
||||
var codeGenInclusionArgs = function (tag) {
|
||||
var args = null;
|
||||
|
||||
if ('content' in tag) {
|
||||
args = (args || {});
|
||||
args.__content = (
|
||||
'UI.block(' + Spacebars.compile2(tag.content) + ')');
|
||||
}
|
||||
if ('elseContent' in tag) {
|
||||
args = (args || {});
|
||||
args.__elseContent = (
|
||||
'UI.block(' + Spacebars.compile2(tag.elseContent) + ')');
|
||||
}
|
||||
|
||||
_.each(tag.args, function (arg) {
|
||||
var argType = arg[0];
|
||||
var argValue = arg[1];
|
||||
@@ -1466,8 +1492,9 @@ var codeGenInclusionArgs = function (tag) {
|
||||
|
||||
if (arg.length > 2) {
|
||||
// keyword argument (represented as [type, value, name])
|
||||
var name = arg[2];
|
||||
args = (args || {});
|
||||
args[toJSLiteral(arg[2])] = argCode;
|
||||
args[toJSLiteral(name)] = argCode;
|
||||
} else {
|
||||
// positional argument
|
||||
// XXX deal with >1 posArgs for #foo helpers
|
||||
@@ -1483,6 +1510,7 @@ var codeGenInclusionArgs = function (tag) {
|
||||
};
|
||||
|
||||
Spacebars.include = function (kindOrFunc, args) {
|
||||
args = args || {};
|
||||
if (typeof kindOrFunc === 'function') {
|
||||
// function block helper
|
||||
var func = kindOrFunc;
|
||||
@@ -1495,13 +1523,27 @@ Spacebars.include = function (kindOrFunc, args) {
|
||||
}
|
||||
}
|
||||
|
||||
var result;
|
||||
if ('data' in args) {
|
||||
var data = args.data;
|
||||
data = (typeof data === 'function' ? data() : data);
|
||||
return func(data, { hash: hash });
|
||||
result = func(data, { hash: hash });
|
||||
} else {
|
||||
return func({ hash: hash });
|
||||
result = func({ hash: hash });
|
||||
}
|
||||
// In `{{#foo}}...{{/foo}}`, if `foo` is a function that
|
||||
// returns a component, attach __content and __elseContent
|
||||
// to it.
|
||||
if (UI.isComponent(result) &&
|
||||
(('__content' in args) || ('__elseContent' in args))) {
|
||||
var extra = {};
|
||||
if ('__content' in args)
|
||||
extra.__content = args.__content;
|
||||
if ('__elseContent' in args)
|
||||
extra.__elseContent = args.__elseContent;
|
||||
result = result.extend(extra);
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
// Component
|
||||
var kind = kindOrFunc;
|
||||
@@ -1677,7 +1719,7 @@ var codeGenMustache = function (tag, mustacheType) {
|
||||
(argCode ? ', ' + argCode.join(', ') : '') + ')';
|
||||
};
|
||||
|
||||
Spacebars.compile2 = function (input) {
|
||||
Spacebars.compile2 = function (input, options) {
|
||||
var tree;
|
||||
|
||||
// Accept string or output of Spacebars.parse
|
||||
@@ -1690,7 +1732,19 @@ Spacebars.compile2 = function (input) {
|
||||
|
||||
tree = replaceSpecials(tree);
|
||||
|
||||
var code = '(function () { var self = this; return ';
|
||||
// is this a template, rather than a block passed to
|
||||
// a block helper, say
|
||||
var isTemplate = (options && options.isTemplate);
|
||||
|
||||
var code = '(function () { var self = this; ';
|
||||
if (isTemplate) {
|
||||
// support `{{> content}}` and `{{> elseContent}}` with
|
||||
// lexical scope by creating a local variable in the
|
||||
// template's render function.
|
||||
code += 'var __content = self.__content, ' +
|
||||
'__elseContent = self.__elseContent; ';
|
||||
}
|
||||
code += 'return ';
|
||||
code += UI.toCode(tree);
|
||||
code += '; })';
|
||||
|
||||
|
||||
@@ -150,10 +150,9 @@ html2_scanner = {
|
||||
|
||||
var renderFuncCode = Spacebars.compile2(
|
||||
contents, {
|
||||
sourceName: 'Template "' + name + '"',
|
||||
// XXX MESSY HACK - make only Templates expose
|
||||
// `content` and `elseContent`
|
||||
preamble: '\n var _local_content = this.content = this.__content;\n var _local_elseContent = this.elseContent = this.__elseContent' });
|
||||
isTemplate: true,
|
||||
sourceName: 'Template "' + name + '"'
|
||||
});
|
||||
|
||||
results.js += "\nTemplate[" + JSON.stringify(name) +
|
||||
"] = UI.Component.extend({kind: " +
|
||||
|
||||
@@ -56,6 +56,84 @@ UI.If = Component.extend({
|
||||
}
|
||||
});
|
||||
|
||||
UI.If2 = Component.extend({
|
||||
kind: 'If',
|
||||
init: function () {
|
||||
// XXX this probably deserves a better explanation if this code is
|
||||
// going to stay with us.
|
||||
this.condition = this.data;
|
||||
|
||||
// content doesn't see the condition as `data`
|
||||
delete this.data;
|
||||
// XXX I guess this means it's kosher to mutate properties
|
||||
// of a Component during init (but presumably not before
|
||||
// or after)?
|
||||
},
|
||||
render: function (buf) {
|
||||
var self = this;
|
||||
return function () {
|
||||
var condition = getCondition(self);
|
||||
|
||||
// `__content` and `__elseContent` are passed by
|
||||
// the compiler and are *not* emboxed, they are just
|
||||
// Component kinds.
|
||||
return condition ? self.__content : self.__elseContent;
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// Acts like `!! self.condition()` except:
|
||||
//
|
||||
// - Empty array is considered falsy
|
||||
// - The result is Deps.isolated (doesn't trigger invalidation
|
||||
// as long as the condition stays truthy or stays falsy
|
||||
var getCondition = function (self) {
|
||||
return Deps.isolateValue(function () {
|
||||
// `condition` is emboxed; it is always a function,
|
||||
// and it only triggers invalidation if its return
|
||||
// value actually changes. We still need to isolate
|
||||
// the calculation of whether it is truthy or falsy
|
||||
// in order to not re-render if it changes from one
|
||||
// truthy or falsy value to another.
|
||||
var cond = self.condition();
|
||||
|
||||
// empty arrays are treated as falsey values
|
||||
if (cond instanceof Array && cond.length === 0)
|
||||
return false;
|
||||
else
|
||||
return !! cond;
|
||||
});
|
||||
};
|
||||
|
||||
UI.Unless2 = Component.extend({
|
||||
kind: 'Unless',
|
||||
init: function () {
|
||||
this.condition = this.data;
|
||||
delete this.data;
|
||||
},
|
||||
render: function (buf) {
|
||||
var self = this;
|
||||
return function () {
|
||||
var condition = getCondition(self);
|
||||
return (! condition) ? self.__content : self.__elseContent;
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
UI.With2 = Component.extend({
|
||||
kind: 'With',
|
||||
init: function () {
|
||||
this.condition = this.data;
|
||||
},
|
||||
render: function (buf) {
|
||||
var self = this;
|
||||
return function () {
|
||||
var condition = getCondition(self);
|
||||
return condition ? self.__content : self.__elseContent;
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
UI.Unless = Component.extend({
|
||||
kind: 'Unless',
|
||||
init: function () {
|
||||
|
||||
@@ -693,3 +693,7 @@ UI.body2 = UI.Component.extend({
|
||||
// XXX revisit how body works.
|
||||
INSTANTIATED: false
|
||||
});
|
||||
|
||||
UI.block = function (renderFunc) {
|
||||
return UI.Component.extend({ render: renderFunc });
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user