mirror of
https://github.com/marko-js/marko.git
synced 2025-12-08 19:26:05 +00:00
Fixes #36 Deprecated invokeBody in favor of renderBody
This commit is contained in:
parent
6a2f603ea9
commit
79aa92254f
22
README.md
22
README.md
@ -1383,13 +1383,13 @@ exports.render = function(input, out) {
|
||||
}
|
||||
```
|
||||
|
||||
If, and only if, a tag has nested content, then a special `invokeBody` method will be added to the `input` object. If a renderer wants to render the nested body content then it must call the `invokeBody` method. For example:
|
||||
If, and only if, a tag has nested content, then a special `renderBody` method will be added to the `input` object. If a renderer wants to render the nested body content then it must call the `renderBody` method. For example:
|
||||
|
||||
```javascript
|
||||
exports.render = function(input, out) {
|
||||
out.write('BEFORE BODY');
|
||||
if (input.invokeBody) {
|
||||
input.invokeBody();
|
||||
if (input.renderBody) {
|
||||
input.renderBody(out);
|
||||
}
|
||||
out.write('AFTER BODY');
|
||||
}
|
||||
@ -1531,12 +1531,12 @@ Marko supports this by leveraging JavaScript closures in the compiled output. A
|
||||
"tags": {
|
||||
"ui-tabs": {
|
||||
"renderer": "./tabs-tag",
|
||||
"var": "tabs"
|
||||
"body-function": "getTabs(__tabsHelper)"
|
||||
},
|
||||
"ui-tab": {
|
||||
"renderer": "./tab-tag",
|
||||
"import-var": {
|
||||
"tabs": "tabs"
|
||||
"tabs": "__tabsHelper"
|
||||
},
|
||||
"attributes": {
|
||||
"title": "string"
|
||||
@ -1557,15 +1557,21 @@ var templatePath = require.resolve('./template.marko');
|
||||
var template = require('marko').load(templatePath);
|
||||
|
||||
exports.render = function(input, out) {
|
||||
var nestedTabs = [];
|
||||
var nestedTabs;
|
||||
|
||||
if (input.getTabs) {
|
||||
nestedTabs = [];
|
||||
// Invoke the body function to discover nested <ui-tab> tags
|
||||
input.invokeBody({ // Invoke the body with the scoped "tabs" variable
|
||||
input.getTabs({ // Invoke the body with the scoped "tabs" variable
|
||||
addTab: function(tab) {
|
||||
tab.id = tab.id || ("tab" + tabs.length);
|
||||
nestedTabs.push(tab);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
nestedTabs = input.tabs || [];
|
||||
}
|
||||
|
||||
|
||||
// Now render the markup for the tabs:
|
||||
template.render({
|
||||
@ -1596,7 +1602,7 @@ _components/tabs/template.marko:_
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div id="${tab.id}" class="tab-pane" for="tab in data.tabs">
|
||||
<invoke function="tab.invokeBody()"/>
|
||||
<invoke function="tab.renderBody(out)"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -134,7 +134,7 @@ CodeWriter.prototype = {
|
||||
thisObj = arguments[2];
|
||||
}
|
||||
this.incIndent(delta);
|
||||
func.call(thisObj);
|
||||
func.call(thisObj, this);
|
||||
this.decIndent(delta);
|
||||
} else if (typeof arguments[0] === 'string') {
|
||||
this.code(this._indent + arguments[0]);
|
||||
@ -264,8 +264,8 @@ TemplateBuilder.prototype = {
|
||||
var newWriter = new CodeWriter(this.concatWrites, oldWriter.indentStr());
|
||||
try {
|
||||
this.writer = newWriter;
|
||||
func.call(thisObj);
|
||||
return newWriter.getOutput();
|
||||
var value = func.call(thisObj);
|
||||
return value == null ? newWriter.getOutput() : value;
|
||||
} finally {
|
||||
this.writer = oldWriter;
|
||||
}
|
||||
|
||||
@ -88,6 +88,7 @@ Taglib.Tag = makeClass({
|
||||
this.nestedVariables = {};
|
||||
this.importedVariables = {};
|
||||
this.patternAttributes = [];
|
||||
this.bodyFunction = null;
|
||||
},
|
||||
inheritFrom: function (superTag) {
|
||||
var subTag = this;
|
||||
@ -182,6 +183,12 @@ Taglib.Tag = makeClass({
|
||||
var key = transformer.path;
|
||||
transformer.taglibId = this.taglibId;
|
||||
this.transformers[key] = transformer;
|
||||
},
|
||||
setBodyFunction: function(name, params) {
|
||||
this.bodyFunction = {
|
||||
name: name,
|
||||
params: params
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -7,7 +7,6 @@ try {
|
||||
|
||||
}
|
||||
|
||||
|
||||
var ok = require('assert').ok;
|
||||
var nodePath = require('path');
|
||||
var Taglib = require('./Taglib');
|
||||
@ -18,6 +17,9 @@ var tagDefFromCode = require('./tag-def-from-code');
|
||||
var resolve = require('../util/resolve'); // NOTE: different implementation for browser
|
||||
var propertyHandlers = require('property-handlers');
|
||||
|
||||
var safeVarName = /^[A-Za-z_$][A-Za-z0-9_]*$/;
|
||||
var bodyFunctionRegExp = /^([A-Za-z_$][A-Za-z0-9_]*)(?:\(([^)]*)\))?$/;
|
||||
|
||||
function exists(path) {
|
||||
try {
|
||||
require.resolve(path);
|
||||
@ -192,6 +194,29 @@ function buildTag(tagObject, path, taglib, dirname) {
|
||||
name: value
|
||||
});
|
||||
},
|
||||
bodyFunction: function(value) {
|
||||
var parts = bodyFunctionRegExp.exec(value);
|
||||
if (!parts) {
|
||||
throw new Error('Invalid value of "' + value + '" for "body-function". Expected value to be of the following form: <function-name>([param1, param2, ...])');
|
||||
}
|
||||
|
||||
var functionName = parts[1];
|
||||
var params = parts[2];
|
||||
if (params) {
|
||||
params = params.trim().split(/\s*,\s*/);
|
||||
for (var i=0; i<params.length; i++) {
|
||||
if (params[i].length === 0) {
|
||||
throw new Error('Invalid parameters for body-function with value of "' + value + '"');
|
||||
} else if (!safeVarName.test(params[i])) {
|
||||
throw new Error('Invalid parameter name of "' + params[i] + '" for body-function with value of "' + value + '"');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
params = [];
|
||||
}
|
||||
|
||||
tag.setBodyFunction(functionName, params);
|
||||
},
|
||||
vars: function(value) {
|
||||
if (value) {
|
||||
value.forEach(function(v, i) {
|
||||
|
||||
@ -25,8 +25,8 @@
|
||||
"char-props": "~0.1.5",
|
||||
"events": "^1.0.2",
|
||||
"htmlparser2": "^3.7.2",
|
||||
"marko-async": "^1.2.12",
|
||||
"marko-layout": "^1.1.0",
|
||||
"marko-async": "^2.0.0",
|
||||
"marko-layout": "^2.0.0",
|
||||
"minimatch": "^0.2.14",
|
||||
"property-handlers": "^1.0.0",
|
||||
"raptor-args": "^1.0.0",
|
||||
|
||||
@ -7,6 +7,8 @@ var attrs = require('raptor-util/attrs');
|
||||
var forEach = require('raptor-util/forEach');
|
||||
var markoRegExp = /\.marko(.xml|.html)?$/;
|
||||
var req = require;
|
||||
var arrayFromArguments = require('raptor-util/arrayFromArguments');
|
||||
var logger = require('raptor-logging').logger(module);
|
||||
|
||||
function notEmpty(o) {
|
||||
if (o == null) {
|
||||
@ -20,6 +22,8 @@ function notEmpty(o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var WARNED_INVOKE_BODY = 0;
|
||||
|
||||
module.exports = {
|
||||
s: function(str) {
|
||||
return (str == null) ? '' : str;
|
||||
@ -132,13 +136,27 @@ module.exports = {
|
||||
/**
|
||||
* Invoke a tag handler render function
|
||||
*/
|
||||
t: function (out, renderFunc, input, body) {
|
||||
t: function (out, renderFunc, input, body, hasOutParam) {
|
||||
if (!input) {
|
||||
input = {};
|
||||
}
|
||||
|
||||
if (body) {
|
||||
input.invokeBody = body;
|
||||
input.renderBody = body;
|
||||
input.invokeBody = function() {
|
||||
if (!WARNED_INVOKE_BODY) {
|
||||
WARNED_INVOKE_BODY = 1;
|
||||
logger.warn('invokeBody(...) deprecated. Use renderBody(out) instead.', new Error().stack);
|
||||
}
|
||||
|
||||
if (!hasOutParam) {
|
||||
var args = arrayFromArguments(arguments);
|
||||
args.unshift(out);
|
||||
body.apply(this, args);
|
||||
} else {
|
||||
body.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
renderFunc(input, out);
|
||||
|
||||
@ -34,7 +34,15 @@ function getPropsStr(props, template) {
|
||||
template.indent(function () {
|
||||
forEachEntry(props, function (name, value) {
|
||||
if (typeof value === 'function') {
|
||||
value = value();
|
||||
value = template.captureCode(function() {
|
||||
return value(template);
|
||||
});
|
||||
|
||||
if (!value) {
|
||||
throw new Error('Invalid value for property "' + name + '"');
|
||||
}
|
||||
|
||||
value = template.makeExpression(value);
|
||||
}
|
||||
|
||||
if (template.isExpression(value)) {
|
||||
@ -51,7 +59,6 @@ function getPropsStr(props, template) {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
if (propsArray.length) {
|
||||
return '{\n' + propsArray.join(',\n') + '\n' + template.indentStr() + '}';
|
||||
} else {
|
||||
@ -102,16 +109,24 @@ TagHandlerNode.prototype = {
|
||||
},
|
||||
doGenerateCode: function (template) {
|
||||
template.addStaticVar('__renderer', '__helpers.r');
|
||||
|
||||
var _this = this;
|
||||
var rendererPath = template.getRequirePath(this.tag.renderer); // Resolve a path to the renderer relative to the directory of the template
|
||||
var handlerVar = addHandlerVar(template, rendererPath);
|
||||
var tagHelperVar = template.addStaticVar('__tag', '__helpers.t');
|
||||
var bodyFunction = this.tag.bodyFunction;
|
||||
|
||||
this.tag.forEachImportedVariable(function (importedVariable) {
|
||||
this.setProperty(importedVariable.targetProperty, template.makeExpression(importedVariable.expression));
|
||||
}, this);
|
||||
|
||||
var _this = this;
|
||||
if (bodyFunction) {
|
||||
this.setProperty(bodyFunction.name, function(template) {
|
||||
template.code('function(' + bodyFunction.params + ') {\n').indent(function () {
|
||||
_this.generateCodeForChildren(template);
|
||||
}).indent().code('}');
|
||||
});
|
||||
}
|
||||
|
||||
var variableNames = [];
|
||||
_this.tag.forEachVariable(function (nestedVar) {
|
||||
var varName;
|
||||
@ -165,14 +180,32 @@ TagHandlerNode.prototype = {
|
||||
|
||||
template.code(getPropsStr(_this.getProperties(), template));
|
||||
}
|
||||
if (_this.hasChildren()) {
|
||||
if (_this.hasChildren() && !_this.tag.bodyFunction) {
|
||||
var bodyParams = [];
|
||||
var hasOutParam = false;
|
||||
|
||||
variableNames.forEach(function (varName) {
|
||||
if (varName === 'out') {
|
||||
hasOutParam = true;
|
||||
}
|
||||
bodyParams.push(varName);
|
||||
});
|
||||
template.code(',\n').line('function(' + bodyParams.join(',') + ') {').indent(function () {
|
||||
|
||||
var params;
|
||||
|
||||
if (hasOutParam) {
|
||||
params = bodyParams.join(',');
|
||||
} else {
|
||||
params = 'out' + (bodyParams.length ? ',' + bodyParams.join(',') : '');
|
||||
}
|
||||
|
||||
template.code(',\n').line('function(' + params + ') {').indent(function () {
|
||||
_this.generateCodeForChildren(template);
|
||||
}).indent().code('}');
|
||||
|
||||
if (hasOutParam) {
|
||||
template.code(',\n').code(template.indentStr() + '1');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -60,7 +60,7 @@ function testRender(path, data, done, options) {
|
||||
|
||||
}
|
||||
|
||||
describe('marko/marko' , function() {
|
||||
describe('marko/render' , function() {
|
||||
|
||||
beforeEach(function(done) {
|
||||
// for (var k in require.cache) {
|
||||
|
||||
@ -13,3 +13,17 @@
|
||||
Tab 3 content
|
||||
</test-tab>
|
||||
</test-tabs>
|
||||
|
||||
<test-tabs-new>
|
||||
<test-tab-new title="Tab 1">
|
||||
Tab 1 content
|
||||
</test-tab-new>
|
||||
|
||||
<test-tab-new title="Tab 2">
|
||||
Tab 2 content
|
||||
</test-tab-new>
|
||||
|
||||
<test-tab-new title="Tab 3" if="showConditionalTab">
|
||||
Tab 3 content
|
||||
</test-tab-new>
|
||||
</test-tabs-new>
|
||||
@ -1 +1 @@
|
||||
<div class="tabs"><ul class="nav nav-tabs"><li class="active"><a href="#tab0" data-toggle="tab">Tab 1</a></li><li><a href="#tab1" data-toggle="tab">Tab 2</a></li></ul><div class="tab-content"><div id="tab0" class="tab-pane active">Tab 1 content</div><div id="tab1" class="tab-pane">Tab 2 content</div></div></div>
|
||||
<div class="tabs"><ul class="nav nav-tabs"><li class="active"><a href="#tab0" data-toggle="tab">Tab 1</a></li><li><a href="#tab1" data-toggle="tab">Tab 2</a></li></ul><div class="tab-content"><div id="tab0" class="tab-pane active">Tab 1 content</div><div id="tab1" class="tab-pane">Tab 2 content</div></div></div><div class="tabs"><ul class="nav nav-tabs"><li class="active"><a href="#tab0" data-toggle="tab">Tab 1</a></li><li><a href="#tab1" data-toggle="tab">Tab 2</a></li></ul><div class="tab-content"><div id="tab0" class="tab-pane active">Tab 1 content</div><div id="tab1" class="tab-pane">Tab 2 content</div></div></div>
|
||||
@ -26,6 +26,19 @@
|
||||
"title": "string"
|
||||
}
|
||||
},
|
||||
"test-tabs-new": {
|
||||
"renderer": "./tabs-new-tag.js",
|
||||
"body-function": "buildTabs(__tabsHelper)"
|
||||
},
|
||||
"test-tab-new": {
|
||||
"renderer": "./tab-new-tag.js",
|
||||
"import-var": {
|
||||
"tabs": "__tabsHelper"
|
||||
},
|
||||
"attributes": {
|
||||
"title": "string"
|
||||
}
|
||||
},
|
||||
"test-dynamic-attributes": {
|
||||
"renderer": "./dynamic-attributes-tag.js",
|
||||
"attributes": {
|
||||
|
||||
4
test/test-project/tab-new-tag.js
Normal file
4
test/test-project/tab-new-tag.js
Normal file
@ -0,0 +1,4 @@
|
||||
exports.render = function(input, out) {
|
||||
var tabs = input.tabs;
|
||||
tabs.addTab(input);
|
||||
};
|
||||
33
test/test-project/tabs-new-tag.js
Normal file
33
test/test-project/tabs-new-tag.js
Normal file
@ -0,0 +1,33 @@
|
||||
var marko = require('../../');
|
||||
|
||||
exports.render = function(input, out) {
|
||||
var tabs = [],
|
||||
activeFound = false;
|
||||
|
||||
if (input.buildTabs) {
|
||||
input.buildTabs({
|
||||
addTab: function(tab) {
|
||||
if (tab.active) {
|
||||
tab.activeFound = true;
|
||||
}
|
||||
|
||||
tab.id = "tab" + tabs.length;
|
||||
tabs.push(tab);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!activeFound && tabs.length) {
|
||||
tabs[0].active = true;
|
||||
}
|
||||
|
||||
tabs.forEach(function(tab) {
|
||||
tab.liClass = tab.active ? "active" : "";
|
||||
tab.divClass = tab.active ? "tab-pane active" : "tab-pane";
|
||||
});
|
||||
|
||||
marko.render(require.resolve('./tabs.marko'), {
|
||||
tabs: tabs
|
||||
}, out);
|
||||
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user