Для сравнения была поставлена задача преобразовать входные данные вида:
var data = {
caption: "Cap",
people: [ 'John', 'Malkovich', 'Doe' ]
}
в строку Cap: John, Malkovich, Doe
См. также Сравнение возможностей веб-шаблонизаторов, EN
#ifdef( CAPTION )
CAPTION:
#endif
# no vm, no loops
m4_ifdef( `CAPTION', `CAPTION: ')', )
m4_dnl no loops?
#if( $caption )
$caption:
#endif
#foreach( $person in $people )
$person
#end
<?= $caption ?>
<? if ($caption): ?>: <? endif ?>
<? foreach ($people as $i => $person): ?>
<?= $person ?>
<? if ($i < count($people) - 1): ?>, <? endif ?>
<? endforeach ?>
<%= caption %>
<% if (caption) { %>: <% } %>
<% for (var i = 0; i < people.length; i++) { %>
<%= people[i] %>
<% if (i < people.length - 1) { %>, <% } %>
<% } %>
<%= caption %>
<% if (caption) %>: <% end %>
<% @people.each do |person, i| -%>
<%= person %>
<% if (i < people.length - 1) %>, <% end %>
<% end -%>
{{caption}}
{{if caption}}: {{/if}}
{{for i:person in people}}
{{person}}
{{if i < people.length}}, {{/if}}
{{/for}}
{$caption}
{if !$caption}: {/if}
{foreach from=$people item=person}
{$person}
{if !$smarty.foreach.last}, {/if}
{/if}
[% caption %]
[% IF caption %]: [% END %]
[% FOREACH person IN people %]
[% person %]
[% IF !loop.last %], [%END%]
[% END %]
{{caption}}
{% if caption %}: {% endif %}
{% for person in people %}
{{ person }}
{% if not loop.last %}, {% endif %}
{% endfor %}
{{caption}}
{{#caption}}: {{/caption}}
{{#people}}
{{.}}
{{^last}}, {{/last}}
{{/people}}
{caption}
{#caption}: {/caption}
{#people}
{.}
{@sep}, {/sep}
{/people}
<%# DataBinder.Eval(Container, "caption") %>
<asp:datalist id="people"><%# DataBinder.Eval(Container, "person") %></asp:repeater>
<fest:template xmlns:fest="http://fest.mail.ru" context_name="json">
<fest:value>json.caption</fest:value>
<fest:if test="json.caption">:<fest:space /></fest:if>
<fest:for iterate="json.people" index="i" value="person">
<fest:value>person</fest:value>
<fest:if test="i < json.people.length - 1">,<fest:space /></fest:if>
</fest:for>
</fest:template>
- if (caption)
= caption + ': '
- each person, i in people
= person
- if (i < people.length - 1)
= ', '
- if (caption)
= caption + ': '
= @people.join(', ')
<?xml version="1.0"?>
<data>
<caption>Cap</caption>
<people>
<person>John</person>
<person>Doe</person>
</people>
</data>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/data/caption">
<xsl:value-of select="." />:
</xsl:template>
<xsl:template match="/data/people/person">
<xsl:value-of select="." />,
</xsl:template>
<xsl:template match="/data/people/person[position() = last()]">
<xsl:value-of select="." />
</xsl:template>
</xsl:stylesheet>
http://jsfiddle.net/qfox/4ye3fLgc/1/
var transform = [
{ tag : 'h1', html : function (v) {
return v.caption ? v.caption + ': ' : '';
} },
{ tag : 'section', children : function (ctx) {
return json2html.transform(ctx.people, {
tag : 'span', html : function (person, i) {
return person + (i < ctx.people.length - 1 ? ', ' : '');
}
} );
} }
];
document.body.innerHTML = json2html.transform(data, transform);
<h1>Cap: </h1>
<section>
<span>John, </span>
<span>Malkovich, </span>
<span>Doe</span>
</section>
div.root
h1.caption
section.people
span.person
$('.root').render({
"caption": "Cap",
"people": [
{ "person": "John" }, // unfair
{ "person": "Malkovich" },
{ "person": "Doe" }
]
});
<div class="root">
<h1 class="caption">Cap</h1>
<section class="people">
<span class="person">John</span>
<span class="person">Malkovich</span>
<span class="person">Doe</span>
</section>
</div>
http://www.javascriptoo.com/Plates
var tmpl = '<h1 class="caption"></h1><span class="person"></span>';
var map = Plates.Map();
map.class('caption').to(function(v, k) { return v[k] ? v[k] + ': ' : ''; });
map.class('person').to(function(v, k) {
if (!v[k]) { // hacky ;-(
if (!v.people) return null;
this._left = v.people.length;
return v.people;
}
return v[k] + (--this._left ? ', ' : '');
});
var res = Plates.bind(tmpl, data, map);
<h1 class="caption">Cap: </h1><span class="person">John, </span><span class="person">Malkovich, </span><span class="person">Doe</span>
http://jsfiddle.net/qfox/t78k9wL5/
var tmpl = '<div><h1 class="caption"></h1><span class="person"></span></div>';
var directive = {
'.caption': function () {
var v = this.caption;
return v ? v + ': ' : '';
},
'.person': {
'person<-people': {
'.': function (ctx) {
return ctx.item + (ctx.pos < ctx.length - 1 ? ', ' : '');
}
}
}
};
document.body.innerHTML = tmpl;
$p('div').render(data, directive);
<div>
<h1 class="caption">Cap: </h1>
<span class="person">John, </span>
<span class="person">Malkovich, </span>
<span class="person">Doe</span>
</div>
http://jsfiddle.net/qfox/suv4d99r/1/
<div ng-app="root">
<div ng-controller="scope">
<h1 ng-if="caption">{{caption}}: </h1>
<span ng-repeat="person in people" ng-class="{'with-comma': !$last}">{{person}}</span>
</div>
</div>
function scope($scope) {
$scope.caption = "Cap";
$scope.people = [ 'John', 'Malkovich', 'Doe' ];
}
angular.module('root', []);
.with-comma:after { content: ", "; } /* tricky ;-( */
Result:
<div ng-app="root" class="ng-scope">
<div ng-controller="scope" class="ng-scope">
<h1 ng-if="caption" class="ng-binding">Cap: </h1>
<!-- ngRepeat: person in people -->
<span ng-repeat="person in people" ng-class="{'with-comma':!$last}" class="ng-scope ng-binding with-comma">foo</span>
<span ng-repeat="person in people" ng-class="{'with-comma':!$last}" class="ng-scope ng-binding">bar</span>
</div>
</div>
<h1 data-bind="if: caption"><!-- ko text: caption --><!-- /ko -->: </h1>
<section data-bind="foreach: people">
<!-- ko text: $data --><!-- /ko -->
<!-- ko if: ($index() < ($parent.people().length - 1)) -->, <!-- /ko -->
</section>
function PeopleViewModel(data) {
this.caption = ko.observable(data.caption);
this.people = ko.observableArray(data.people);
}
ko.applyBindings(new PeopleViewModel(data));
<h1 data-bind="if: caption"><!-- ko text: caption -->Cap<!-- /ko -->: </h1>
<section data-bind="foreach: people">
<!-- ko text: $data -->John<!-- /ko -->
<!-- ko if: ($index() < ($parent.people().length - 1)) -->, <!-- /ko -->
<!-- ko text: $data -->Malkovich<!-- /ko -->
<!-- ko if: ($index() < ($parent.people().length - 1)) -->, <!-- /ko -->
<!-- ko text: $data -->Doe<!-- /ko -->
<!-- ko if: ($index() < ($parent.people().length - 1)) --><!-- /ko -->
</section>
http://wix.github.io/react-templates/fiddle.html#88d931fc
var template = React.createClass({
render: function () {
return templateRT.apply(this);
}
});
<div>
<h1 rt-if="data.caption">{data.caption}: </h1>
<span rt-repeat="person in data.people">
{person}
<span rt-if="personIndex < data.people.length - 1">, </span>
</span>
</div>
<div data-reactid=".2"><h1 data-reactid=".2.0"><span data-reactid=".2.0.0">Cap</span><span data-reactid=".2.0.1">: </span></h1><span data-reactid=".2.1:0"><span data-reactid=".2.1:0.0">John</span><span data-reactid=".2.1:0.1">, </span></span><span data-reactid=".2.1:1"><span data-reactid=".2.1:1.0">Malkovich</span><span data-reactid=".2.1:1.1">, </span></span><span data-reactid=".2.1:2"><span data-reactid=".2.1:2.0">Doe</span></span></div>
https://jsfiddle.net/qfox/4ymugqw2/
jsx:
var TemplateRT = React.createClass({
render: function() {
var data = this.props.data;
return <div>
{data.caption ? data.caption + ': ' : ''}
{data.people.map(function(person, personIndex, people) {
return person +
(personIndex < people.length - 1 ? ', ' : '');
})}
</div>;
}
});
React.render(<TemplateRT data={data} />, document.body);
js:
var TemplateRT = React.createClass({displayName: "TemplateRT",
render: function() {
var data = this.props;
return React.createElement("div", null,
data.caption ? data.caption + ': ' : '',
data.people.map(function(person, personIndex, people) {
return person +
(personIndex < people.length - 1 ? ', ' : '');
})
);
}
});
React.render(React.createElement(TemplateRT, {data: data}), document.body);
<div data-reactid=".0"><span data-reactid=".0.0">Cap: </span><span data-reactid=".0.1:0">John, </span><span data-reactid=".0.1:1">Malkovich, </span><span data-reactid=".0.1:2">Doe</span></div>
([
{ block: 'caption', content: 'Cap' },
{ block: 'people', content: [
{ elem: 'person', content: "John" },
{ elem: 'person', content: "Doe" }
] }
])
bh.beforeEach(function (ctx, json) {
// we need a string in result
ctx.tag(false);
});
bh.match('caption', function (ctx) {
return ctx.content() + ': ';
});
bh.match('people__person', function (ctx) {
return ctx.content() + (ctx.isLast() ? '' : ', ');
});
NB: BH is a BEM specific template engine aimed to generate HTML. But it's possible to convert raw data right in place into structured json:
bh.beforeEach(function (ctx, json) {
// code below needed to make bemjson for raw data only
if (json.content) return;
ctx.content([
json.caption && { block: 'caption', content: json.caption },
json.people && { block: 'people', content: json.people.map(function (person) {
return { elem : 'person', content: person };
})}
]);
});
([
{ block: 'caption', content: 'Cap' },
{ block: 'people', content: [
{ elem: 'person', content: "John" },
{ elem: 'person', content: "Doe" }
] }
])
block('caption')(
tag()(false),
content()(this.ctx.content + ': ')
)
block('people')(
tag()(false),
content()(function () {
return this.ctx.content
.map(function(el) { return el.content })
.join(', ')
})
)
template(this.caption)(function () {
return this.caption + ': ';
});
template(this.people)(function () {
return this.people.join(', '); // unfair
});
- bemhtml: https://ru.bem.info/technology/bemhtml/
- bh: https://bem.github.io/bh/
- ejs: http://www.embeddedjs.com/
- fest: https://github.com/mailru/fest
- haml: http://haml.info/tutorial.html
- handlebars: http://handlebarsjs.com/
- histone: http://weblab.megafon.ru/histone/documentation/
- jade: http://jade-lang.com/
- json2html: http://www.json2html.com/
- knockoutjs: http://knockoutjs.com/
- m4: https://github.com/datagrok/m4-bakery
- plates: https://github.com/flatiron/plates
- pure: http://beebole.com/pure/
- react jsx: https://facebook.github.io/react/docs/jsx-in-depth.html
- react-templates: https://github.com/wix/react-templates
- transparency: http://leonidas.github.io/transparency/
- velocity: http://velocity.apache.org/engine/devel/docs/user-guide.html
- xjst: http://veged.github.io/xjst/
Теперь BH кажется не таким уж и странным, а очень даже удобным)