📜 ⬆️ ⬇️

cnCt - client js template engine

Sooner or later, templateization moves to the client. At the moment there are several generally accepted client templating engines, they are mustache and underscore.template or something like that (you can find more in Sergey Berezhnoy’s report ( s )). Despite the huge number of client shabnizator most, with rare exceptions , are lowercase.


Problems of lowercase templating - working with strings


It's really awful.
First , the templates are redundant (about the relation to html will be disclosed below) and mash. Even in promotional examples

var template = '<div class="entry"><h1>{{title}}</h1><div class="body">{{body}}</div></div>'; 

In the templates, we observe unformatted html, and a certain metalanguage, the support of which needs to be somehow introduced into our environment (thanks and not thanks to JetBrains for the timely support of hipsters metalanguages). And after running through the regulars, all this is inserted using innerHTML , and if you wrote a less-than-large application, you understand that template inserts occur constantly in different places of the tree, and it is simply impossible to reduce it to one insert of all templates. And frequent insertions through innerHTML is hell (in the future I plan to post interesting tests about this).
')
Although it is worth admitting that the formatting problem is partly solved by inserting a template into an undefined script tag, which in turn creates a new problem: all your application templates end up on index.html (php?), And when you type a lot of them, you have to write smart collectors, because phpStorm ' it's sad even with 32GB of RAM.

Second , the problems of working with logic

somewhere so

 {{#repo}} <b>{{name}}</b> {{/repo}} {{^repo}} No repos :({ {/repo}} 

somewhere in the lines (I hope the environments already know how to work with this, otherwise I am very sorry for those who work with it)

 var list = "<% _.each(people, function(name) { %> <li><%= name %></li> <% }); %>"; _.template(list, {people: ['moe', 'curly', 'larry']}); // "<li>moe</li><li>curly</li><li>larry</li>" 

somewhere in separate functions that collect strings

 {{#list people}}{{firstName}} {{lastName}}{{/list}} { people: [ {firstName: "Yehuda", lastName: "Katz"}, {firstName: "Carl", lastName: "Lerche"}, {firstName: "Alan", lastName: "Johnson"} ] } Handlebars.registerHelper('list', function(items, options) { var out = "<ul>"; for(var i=0, l=items.length; i<l; i++) { out = out + "<li>" + options.fn(items[i]) + "</li>"; } return out + "</ul>"; }); 

(my first experience of client templating by the method from Stepan Reznikov is involuntarily remembered)

Third , search for items. The problem is not so terrible as the previous two, but somehow I do not understand why I should look for what I just did?

It usually looks like this.

 var templateWrapper = document.getElementById('templateWrapper'); something.render(template, templateWrapper); var templateElemet = templateWrapper.getElementsByClassName('templateElement'); //      

_small_ (Partly upsets the lack of the ability to properly use the template in the template, although it may have missed the topic due to the deep frustration caused by the above.) _ / small_

cnCt


The main advantages of cnCt are building a DOM using DOM methods and describing a template in the form of json .

jsfiddle example

 //  var template = function(data){ // 'e'  ,   div return {c: 'user', C: [ {e: 'a', h: 'http://facebook.com/' + data.facebookId, c: 'user-avatar-wrapper', C: {e: 'img', c: 'user-avatar', S: 'http://graph.facebook.com/' + data.facebookId + '/picture?width=200&height=200'} }, {c: 'user-name', t: data.fristName + ' ' + data.lastName, n: 'userName'}, ]}; }; // .  tp     var build = cnCt.tp(template, {fristName: 'swf', lastName: 'dev', facebookId: '100005155868851'}, document.body); //       build.r.addEventListener('click', function(){console.log('hello')}); //build.r (r   root)  div.user build.userName.addEventListener('click', function(){console.log(this.textContent)}); //    n: 'userName' 

When developing complex web interfaces, we try to move away from tags in principle, because we understand html as a block structure of a view, so we use only a / input / textarea / (...) tags as the basis of the necessary functionality for a block of view, as a result of which such an approach has been developed.

In battle, it looks like this (jsfiddle)

 //    function dataToStr(_date){ var date = _date instanceof Date ? _date : new Date(_date); return date.getDate() + '/' + date.getMonth() + '/' + date.getFullYear(); } // function l10n(key){ return l10Keys[key] || key; } var l10Keys = { youFriendsList: '  ', sendMessage: ' ' }, //  templates = { // basis: function(){ return [ // {c: 'header', n: 'header', t: l10n('youFriendsList')}, {c: 'content-view', n: 'contentView'}, {c: 'footer', t: ' SoftWear LLC'} ]; }, //     icon: {c: 'icon'}, // button: function(data){ return {c: 'button', t: data.t, n: data.n, C: templates.icon, a: data.a}; }, //  friendsList: function(friends){ var items = [], i = 0, iMax = friends.length; // js   (  ,     ) for(; i < iMax; i += 1){ items[i] = {c: 'friend', C: [ {e: 'a', h: 'http://facebook.com/' + friends[i].facebookId, c: 'friend-avatar-wrapper', C: //     {e: 'img', c: 'friend-avatar', S: 'http://graph.facebook.com/' + friends[i].facebookId + '/picture?width=200&height=200'} }, {c: 'friend-name', t: friends[i].firstName + ' ' + friends[i].lastName}, //   {c: 'friend-birthday', t: dataToStr(friends[i].birthday)}, //   templates.button({n: 'sendMessage', t: l10n('sendMessage'), a: {'data-friend-id': friends[i].facebookId}}) ]}; } return {c: 'friend-list', C: items}; } }, //   friends = [ { facebookId: 1, firstName: '', lastName: '', birthday: 1 }, { facebookId: 2, firstName: '', lastName: '', birthday: 111111111 }, { facebookId: 3, firstName: '', lastName: '', birthday: 123123123 } ], build, $contentView, $buttons, i; //       (      ) cnCt.bindTemplates(templates); //  $contentView = cnCt.tp('basis', document.body).contentView; function getByFacebookId(id){ var i = friends.length; id = +id; for (; i-- ;){ if (friends[i].facebookId === id){ return friends[i]; } } return null; } $buttons = cnCt.tp('friendsList', friends, $contentView).sendMessage; //    ..   function sendMessage(){ var friend = getByFacebookId(this.getAttribute('data-friend-id')); console.log('hello ' + friend.firstName); } for (i = friends.length; i-- ;){ $buttons[i].addEventListener('click', sendMessage); } 


Pros:
  1. Pure js
  2. Abstraction from html
  3. Much faster lowercase on mobile, faster everywhere except opera and not on desktops (I do visualization of tests), and faster on TV and kiosks :)
  4. Templates in Templates
  5. Search inside and not tied to the names of classes or id
  6. Self-sufficient
  7. Auto light and cc in everything that js understands without plugins
  8. supports inline SVG and VML, because supports .createElementNS ()
  9. XML leaves your life
  10. It works in life ( access to the latter ), though not in the selected form, we will respectively support

Minuses:
  1. Slower in IE (mobile phones, kiosks (we transfer them to Chrome) and TV sets are more important to us)
  2. It seems incomprehensible (after a week of use, html seems incomprehensible)
  3. Poorly documented (everything will be soon)
  4. Useless on the node (because not for him / although you can write a branch cnCt json to str, but why?)


We will document and develop.

PS: We decided to open it. colleagues howled from underscore when working with a project that was not done with us.

Source: https://habr.com/ru/post/202432/


All Articles