I have developed a templates engine that parses and executes templates. The syntax is very similar to PHP Smarty templates.
The template source is compiled to a function. When the template is rendered, the function is executed and the output is returned. The workflow is very common.
The function that is generated for templates uses WITH in order to be able to easily use any member of the template rendering object.
It looks something like:
function anonymous( TplData ){ var buffer = []; TplData = TplData || {}; with ( TplData ) { buffer.push( "<" ); buffer.push( tag ); buffer.push( ">" );
<div class="${baseCls}-inner" style="top: -${getSecondLayoutSize()}px; border-top-width: ${getFirstLayoutSize() + getSecondLayoutSize()}px;"> .... other template code here
This gives me a lot of flexibility, the ability to have any kind of valid javascript expressions in the template, etc.
The problem is the following: I'm using WITH, and the new ECMAScript standards, and the strict mode do not support it. Are there any alternatives for this implementation?
Why do you use with in the first place?
Writing:
var t = TplData;
buffer.push( "<" );
buffer.push( t.tag );
buffer.push( ">" );
buffer.push(t.firstName + t.lastName);
buffer.push(t["myDynamicVariable"]);
> I have developed a templates engine that parses and executes templates.
> The syntax is very similar to PHP Smarty templates.
> The template source is compiled to a function. When the template is
> rendered, the function is executed and the output is returned. The workflow
> is very common.
> The function that is generated for templates uses WITH in order to be able
> to easily use any member of the template rendering object.
> It looks something like:
> function anonymous( TplData ){
> var buffer = [];
> TplData = TplData || {};
> with ( TplData ) {
> buffer.push( "<" );
> buffer.push( tag );
> buffer.push( ">" );
> <div class="${baseCls}-inner" style="top: -${getSecondLayoutSize()}px;
> border-top-width: ${getFirstLayoutSize() + getSecondLayoutSize()}px;">
> .... other template code here
> This gives me a lot of flexibility, the ability to have any kind of valid
> javascript expressions in the template, etc.
> The problem is the following: I'm using WITH, and the new ECMAScript
> standards, and the strict mode do not support it. Are there any alternatives
> for this implementation?
> Thank you for any suggestions!
> --
> To view archived discussions from the original JSMentors Mailman list:
> http://www.mail-archive.com/jsment...@jsmentors.com/
Well, yes, that would also do the trick in this simple case. Let me explain:
the template: '<${tag}> ${firstName+lastName} </${tag}>'
Is compiled to: function (tplData){
var buffer = []; with (TplData){ buffer.push('<'); buffer.push(tag); buffer.push('>'); buffer.push(firstName+lastName); buffer.push('</'); buffer.push(tag); buffer.push('>');
}
return buffer.join('');
}
Ok, this could be compiled using TplData.tag, TplData.firstName, just as you mentioned. But this complicates things a lot. In the above template, the most complex js expression is firstName+lastName, but we could have any valid js expression there.
Furthermore, the templates have a mechanism to embed any js code, like:
${do var add5 = function(a){ return a + 5} }
and then I can use:
${add5(age) }
where age would be a property of the template rendering object.
The ${do ... } content is simply thrown into the compiled function AS IS, so we get the above to compile to
with (TplData){ ... //any previous buffer.push() var add5 = function(a){ return a + 5} buffer.push(add5(age));
}
....
Using WITH allows me not to parse that code inside ${do ... }, but simply dump it into the body of the function. Otherwise, I would have to implement a javascript parser, or take one like Esprima. The templates have to be fast, as they are compiled at run-time. We have an option to compile them on the server, and just send the compiled function, but that's another story.
So WITH is definitely needed in my opinion. Do you see any way to avoid it?
On Tuesday, November 6, 2012 2:29:00 PM UTC+2, Peter Galiba wrote:
> Hi,
> Why do you use with in the first place? > Writing: > var t = TplData; > buffer.push( "<" ); > buffer.push( t.tag ); > buffer.push( ">" ); > buffer.push(t.firstName + t.lastName); > buffer.push(t["myDynamicVariable"]);
> would also do the trick.
> 2012/11/6 Radu Brehar <ra...@evanghelic.ro <javascript:>>: > > Hi,
> > I have developed a templates engine that parses and executes templates. > > The syntax is very similar to PHP Smarty templates.
> > The template source is compiled to a function. When the template is > > rendered, the function is executed and the output is returned. The > workflow > > is very common.
> > The function that is generated for templates uses WITH in order to be > able > > to easily use any member of the template rendering object.
> > <div class="${baseCls}-inner" style="top: -${getSecondLayoutSize()}px; > > border-top-width: ${getFirstLayoutSize() + getSecondLayoutSize()}px;"> > > .... other template code here
> > This gives me a lot of flexibility, the ability to have any kind of > valid > > javascript expressions in the template, etc.
> > The problem is the following: I'm using WITH, and the new ECMAScript > > standards, and the strict mode do not support it. Are there any > alternatives > > for this implementation?
> > Thank you for any suggestions!
> > -- > > To view archived discussions from the original JSMentors Mailman list: > > http://www.mail-archive.com/jsment...@jsmentors.com/
Bit hacky, but the Function constructor might help.
// pull an array of argument names from a template object's properties function getArguments(tplData) { var args = []; for (var key in tplData) { if (Object.prototype.hasOwnProperty.call(tplData, key)) { args.push(key); } } return args;
}
// get the values for each of the argument names function values(obj, keys) { var vals = []; for(var i = 0; i < keys.length; i++) { vals.push(obj[keys[i]]); } return vals;
}
Then your template can compile using strings:
function compile(templateStr) { .... var compiledBody = '' /*compiled body string*/;
return function(tplData) { var args = getArguments(tplData); var vals = values(tplData, args);
// pass in a comma-separated string of argument names to use as function parameters var compiledFn = new Function(args.join(','), compiledBody);
// call the compiled function passing in the tplData values for each argument return compiledFn.apply(null, vals); };
} On Tuesday, 6 November 2012 22:02:54 UTC+11, Radu Brehar wrote:
> Hi,
> I have developed a templates engine that parses and executes templates. > The syntax is very similar to PHP Smarty templates.
> The template source is compiled to a function. When the template is > rendered, the function is executed and the output is returned. The workflow > is very common.
> The function that is generated for templates uses WITH in order to be > able to easily use any member of the template rendering object.
> It looks something like:
> function anonymous( TplData ){ > var buffer = []; > TplData = TplData || {}; > with ( TplData ) { > buffer.push( "<" ); > buffer.push( tag ); > buffer.push( ">" );
> <div class="${baseCls}-inner" style="top: -${getSecondLayoutSize()}px; > border-top-width: ${getFirstLayoutSize() + getSecondLayoutSize()}px;"> > .... other template code here
> This gives me a lot of flexibility, the ability to have any kind of valid > javascript expressions in the template, etc.
> The problem is the following: I'm using WITH, and the new ECMAScript > standards, and the strict mode do not support it. Are there any > alternatives for this implementation?
1. The template can use functions from the template rendering object. In your approach, every property/function for the compiled template function are passed as function parameters. So when calling any function, it's called without the correct context, so it won't work correctly. Yet this can be fixed by binding the scope of these functions to the template rendering object. Nevertheless, when the rendering object has many functions on it, this might be a performance hit.
2. Assignments won't work for properties of the template rendering object. Probably not a very big issue, since it's not a good idea for templates to modify properties on the rendering object. Yet it could be an issue if we want to allow for maximum flebixility.
3. I am a bit concerned with the performance of getArguments() and values() function, when used on large rendering objects, with tens of properties/functions. I haven't tested it, but my first presumption is that it's slower than using WITH.
4. Compiled template functions are bigger in size. They are kept in memory, this wouldn't be an issue, but we also offer the possibility to compile templates in the build process and only ship to the browser the compiled version. Using this approach, passing say 100 parameters to the function means more bytes over the wire (100 for the call, and the same 100 arguments declared in function signature).
So the main points that still stand as real issues for me, are 1, 3 and 4. What do you think on this?
I still find the WITH solution more elegant and wish it was not deprecated with the new ECMAScript standards/strict mode.
Can anyone think of any other alternatives?
Thanks in advance!
miercuri, 7 noiembrie 2012, 12:42:20 UTC+2, Adam a scris:
> Bit hacky, but the Function constructor might help.
> // pull an array of argument names from a template object's properties > function getArguments(tplData) { > var args = []; > for (var key in tplData) { > if (Object.prototype.hasOwnProperty.call(tplData, key)) { > args.push(key); > } > } > return args; > }
> // get the values for each of the argument names > function values(obj, keys) { > var vals = []; > for(var i = 0; i < keys.length; i++) { > vals.push(obj[keys[i]]); > } > return vals; > }
> Then your template can compile using strings:
> function compile(templateStr) { > .... > var compiledBody = '' /*compiled body string*/;
> return function(tplData) { > var args = getArguments(tplData); > var vals = values(tplData, args);
> // pass in a comma-separated string of argument names to use as > function parameters > var compiledFn = new Function(args.join(','), compiledBody);
> // call the compiled function passing in the tplData values for > each argument > return compiledFn.apply(null, vals); > }; > }
> On Tuesday, 6 November 2012 22:02:54 UTC+11, Radu Brehar wrote:
>> Hi,
>> I have developed a templates engine that parses and executes templates. >> The syntax is very similar to PHP Smarty templates.
>> The template source is compiled to a function. When the template is >> rendered, the function is executed and the output is returned. The workflow >> is very common.
>> The function that is generated for templates uses WITH in order to be >> able to easily use any member of the template rendering object.
>> It looks something like:
>> function anonymous( TplData ){ >> var buffer = []; >> TplData = TplData || {}; >> with ( TplData ) { >> buffer.push( "<" ); >> buffer.push( tag ); >> buffer.push( ">" );
>> <div class="${baseCls}-inner" style="top: -${getSecondLayoutSize()}px; >> border-top-width: ${getFirstLayoutSize() + getSecondLayoutSize()}px;"> >> .... other template code here
>> This gives me a lot of flexibility, the ability to have any kind of valid >> javascript expressions in the template, etc.
>> The problem is the following: I'm using WITH, and the new ECMAScript >> standards, and the strict mode do not support it. Are there any >> alternatives for this implementation?