Is it possible to insert new HTML with pure.js - javascript

I am searching for something which renders an HTML template starting from a JSON file of data.
The matter is that the plugin/framework/library I'm searching for must create itself the <html> template structure, starting from something very simple.
For example I have an simple html like this:
<ul>
<li><li>
</ul>
and a json like this:
{
"mylist":{
"listone":
{"img" : "/img/pippo.gif" ,
"text1" : "pluto",
"text2" : "topolino",
"link" : "http://www.sito.it"
},
"listtwo":
{"img" : "/img/pippo.gif" ,
"text1" : "pluto",
"text2" : "topolino",
"link" : "http://www.sito.it"
}
}
}
and I want the data to render in my document like this:
<ul>
<li>
<img src="/img/pippo.gif" />
<h1>pluto</h1>
<p><a href="http:://www.sito.it>topolino</a></p>
</li>
</ul>
If I head already the entire structure I could use pure.js as usual, but, since I don't have the inner tags in the li, can I inject the HTML code with the pure.js directives?
Or is it possible only with JsRender or similar?

Pure JS allows you to use JavaScript function with directives. Whatever is returned from that function, will be used as a value for a directive.
The argument of the function is an object with the following
properties:
context : This is the full JSON that was passed for transformation
item* : the current loop item
items* : all the items of the loop
pos* : the current position in the loop. An integer when iterating an array, a property name iterating on a collection
The following example shows how to do it.
var data = {
"img": "/img/pippo.gif",
"text1": "pluto",
"text2": "topolino",
"link": "http://www.sito.it"
}
var directive = {
'li': function (arg) {
return "<img src=\""+arg.context.img+"\" /><h1>"
+arg.context.text1+"</h1><p><a href=\""
+arg.context.link+"\">"+arg.context.text2+"</a></p>"
}
}
$('ul').render(data, directive);
The given HTML:
<ul><li></li></ul>
Will become as following one (after rendering):
<ul>
<li>
<img src="/img/pippo.gif">
<h1>pluto</h1>
<p>
topolino
</p>
</li>
</ul>
I hope that will help.

This is more advise than answer. I would recommend against what you are trying to accomplish. The powerful concept used for templating is to separate the HTML from the code. When you keep this separation and you can figure out how to write code and understand code that follows this separation. Then code will be easier to write and to understand, not only by you but by others that follow that same principle.
In your example your HTML template should be like this:
<ul>
<li>
<img src="/img/pappo.gif" />
<h1>marte</h1>
<p><a href="http:://www.sito.it>guille</a></p>
</li>
</ul>
and a json like this:
{
"mylist":{
"listone":
{"img" : "/img/pippo1.gif" ,
"text1" : "pluto1",
"text2" : "topolino1",
"link" : "http://www.sito1.it"
},
"listtwo":
{"img" : "/img/pippo2.gif" ,
"text1" : "pluto2",
"text2" : "topolino2",
"link" : "http://www.sito2.it"
}
}
}
and the final should look like this:
<ul>
<li>
<img src="/img/pippo1.gif" />
<h1>pluto1</h1>
<p><a href="http:://www.sito1.it>topolino1</a></p>
</li>
<li>
<img src="/img/pippo2.gif" />
<h1>pluto2</h1>
<p><a href="http:://www.sito2.it>topolino2</a></p>
</li>
</ul>
The easiest way I can explain how to accomplish this is to make your JSON data structure follow your HTML almost one to one. The huge difference is that once that is done the template will be easy to write, and programming the JSON transformation will eventually be easier than writing the HTML transformation. Plus your HTML templates will be 100% render-able by a browser and a 100% modifiable by non programmers. The key here is that although your final product is HTML, taking the JSON object route will produce better code. It won't remove the actual problem of creating the JSON, and you will need to adjust, learn and find tools to do that.

Certainly it's possible. Search for 'javascript DOM createElement' and you'll find plenty of examples (including all over stackoverflow).

Related

Gulp, Moustache. How to concat data with src attribute?

I'm using the gulp-html-i18n gulp plugin for handling the translations task in a static website.
In order to display the translated content, I should use the Moustache lib as gulp-html-i18n mentioned in his doc.
I'm facing difficulties in the concatenation operations. For example, if I need to assign a conditional class for a html element, I will use the following flow:
${{! if}}$
${{#index.isEnglish}}$
<body class="en">
${{/index.isEnglish}}$
${{! else}}$
${{^index.isEnglish}}$
<body class="sp">
${{/index.isEnglish}}$
In the code above, I'm checking if the language is English, for example, then the class is assigned based on the isEnglish value.
By this approach, we will face a big problem if I need to add new language also it's long and not comprehensive.
Moreover, let suppose I have an array of objects in the following interface:
"data":[{
"title": "foo",
"description": "bar",
"icon": "iconname"
},...]
What if I need to concat the icon name with the scr attribute path while looping through the array above?
<div class="content">
${{#index.home.services.data}}$
<h3>
${{title}}$
</h3>
<p>
${{description}}$
</p>
<img src="path/${{}}$.png" alt=""> ---> Not working with Moustache
${{/index.home.services.data}}$
</div>
So how can I solve the problems above? and what is the best practices for the Moustache.js concatenations?
Sloved!
Solved by using scripts of type text/template.
We can't do the concatition operator directly inside a html attribute, We should write it inside a script of type text/template. like the following example:
<script type="text/template" id="header">
${{#index.pages}}$
<button>
${{title}}$
</button>
${{/index.pages}}$
</script>
Then render the script content and assign it to specific html element using the following javascript code:
var headerTemplate = $('#header').html();
var headerHtml = Mustache.to_html(headerTemplate);
$('#navBar').html(headerHtml);
Note: The dollar signs are used based on gulp plugin for localization task gulp-html-i18n
in order to link the json file to mustache.
If I'm not using the mentioned plugin I must update my code to become:
var headerTemplate = $('#header').html();
var headerHtml = Mustache.to_html(headerTemplate,'path to data');
$('#navBar').html(headerHtml);
One of the most helpful link here.

Working with ux-datagrid, angularJS and nested interations

I got it working excellent with one template but ran into an issue trying to replace my inner ng-repeat with another template. Kind of like grouped data or more like nested data.
so let me simplify my html here:
<div data-ng-controller="index.dataGridController" data-ux-datagrid="index.items" class="listA datagrid" data-addons="whichTemplate" grouped="'focus'">
<script type="template/html" data-template-name="default" data-template-item="item">
<div>
<h3>{{ item.obj.name }}</h3>
<!--- instead of
<ul>
<li ng-repeat="listItem in item.focus>{{listitem}}</li>
</uL
-->
</div>
</script>
<script type="template/html" data-template-name="innerFocus" data-template-item="item">
<div>
<ul>
<li>{{item.focus.id}}</li>
</ul>
</div>
</script>
</div>
and the index.items look like this:
[{
"obj": {
"name": "someName"
}
"focus": [
{"id": "something here"},
{"id": "Another etc"}
]
}]
// which template is basically copy and pasted from examples
angular.module('ux').factory('whichTemplate', function () {
return function (inst) {
// now we override it with our method so we decide what template gets displayed for each row.
inst.templateModel.getTemplate = function (item) {
var name = item.focus ? 'innerFocus' : 'default';
// now we get the template from the name.
return inst.templateModel.getTemplateByName(name);
};
};
});
Trying to iterate a list with the focus array. Any ideas? I think it would make for a great example in the docs. Adding a fiddle. http://jsfiddle.net/uftsG/88/
Instead of putting all focus array in one li i'm trying to get them spread out while also having the item.obj.name above the list.
I am not familiar with ux-data grid. If you are set on using it, you can ignore this answer, but I did a small search engine (purely for academic reasons) and i ended up using ng-grid to display the data. I followed the github documentation and ended up boot-strapping #scope.myDefs with the data I needed on the backend using a $scope.getMyData function. In the view all you really need to do is add input fields to get the data, and then a div class with your ng-grid. It's super cool because it ensures the code is modular, and there are no funky functions going on in the view with nested ng-repeats so on and so forth. The only part that gave me trouble was the CSS because I'm not bootstrap savvy yet :) Here's some docs if you end up interested in ng-grid at all.
http://angular-ui.github.io/ng-grid/

Kendo UI - Localize application

How does one localize a pure front-end application that uses a framework such as Kendo UI ?
I mean, it's possible to do something like this:
$(document).ready(function(){
$("#myText").html(<grab text based on language>);
});
But then, if I have a listview and want to localize its title:
<div id="tabstrip-expenseaccounts" data-role="view">
<ul data-role="listview" data-style="inset" data-type="group">
<li id="expenseaccounts-listview-title">
abcde
<ul>
...
</ul>
</li>
</ul>
</div>
Becomes:
...
<li id="expenseaccounts-listview-title" class="km-group-container">
<div class="km-group-title">
<div class="km-text">abcde</div>
</div>
<ul class="km-list">
...
</ul>
</li>
...
I need to inspect the generated code and do something like:
$(document).ready(function(){
$("#expenseaccounts-listview-title.km-group-container div.km-group-title div.km-text").html(<grab text based on language>);
});
It works fine, but that doesn't seem like a clean solution to me.
Any advice ? Thanks!
For KendoUI there some language packs available on GitHub here. This other stakoverflow question should give you a headstart. With this, all you have to do is use the correct language pack and you're good to go. And if there is no language pack for your specific case, you can always roll your own.
Hope this helps.
While I have not found a solution proper to Kendo UI, here is the approach I went for to localize my mobile application. Note here that I am not talking about localizing widgets, I am referring to localizing every static aspect of the application: input placeholders, texts on buttons, headings, etc.
My mobile application only has one file, index.html, and whenever I want to navigate to a different page, i simply move to a different view. Since having multiple views in the same file is kind of a mess, I made one html file per view, and am dynamically loading them into the body (index.html has an empty body). Before appending the html which is retrieved using $.get for each view (at this point, it's a huge string), i am replacing text based on the current language (which is retrieved from the localstorage/cookie or from a default value).
example:
In my localization library:
_localization.localizeText = function(text, arr){
arr.forEach(function(item){
text = text.replace(item.name, getLang() == 1 ? item.replacement.en : item.replacement.fr);
});
return text;
}
In my login.html file:
<button>$$login-button$$</button>
And then in some javascript file which is included before the script in which the application is initialized:
var replacements = [];
replacements.push({
name: "$$login-button$$",
replacement: {
fr: "Connecter",
en: "Log In"
}
});
And then when i'm loading my files into the body:
$.when($.get("login.html"))
.done(function(p1){
var body = $("body");
body.append(localization.localizeText(p1[0], app.replacements));
});
Hope this helps anyone with similar issues!

Interweave EJS and Javascript variables inside <% tags

I need to use a Javascript variable (defined in the front-end) in some EJS code, as follows:
var selected = 1;
<% for (var i=0; i < supplies.length; i++) { %>
if (i == selected) {
console.log(supplies);
}
<% } %>
I'm using EJS, Express.js and socket.io. I could convert the Javascript variable to an EJS variable by sending a message to my Node.js server instance, but that's kind of silly... Is there a way to use Javascript variables inside EJS?
EDIT:
I want to access supplies, a javascript array, after the user selected an item from a drop down menu. When he selects this item, a javascript function with the above code needs to access some EJS. That's why I need to use a normal Javascript variable in EJS.
Can I pass a JavaScript variable into a template?:
It is possible to get more data into the template and re-render it, but not in the manner that you're thinking, and without making another request to the server (unless it's to get more data, not to get more HTML).
Solution:
This question is going to be difficult to answer without more details, so I'm going to make some assumptions about why you want to pass a selected value into an EJS template. I'll do my best to answer this with limited information about your goals.
It seems like your user is performing some action on the page, like selecting a cleaning supply, and you want to render the data differently, based on which element the user selected. To do this, you can re-render the template and pass in data that identifies which element is selected, using a view helper to apply a specific class to the selected element:
Here is the modified cleaning.ejs template, with the class added using the view helper:
cleaning.ejs:
<script>
// NOTE: even if uncommented, JavaScript inside a template will not run.
// var selected = 1;
</script>
<h1><%= title %></h1>
<ul>
<% for(var i=0; i<supplies.length; i++) { %>
<li>
<!-- apply a class to the selected element -->
<a class='<%= supplies[i].selected %>' href='supplies/<%= supplies[i].value %>'>
<%= supplies[i].value %>
</a>
</li>
<% } %>
</ul>
The rendered HTML looks like this:
<script>
/** NOTE: Script blocks will not fire in rendered templates. They are ignored
// var selected = 1;
</script>
<h1>Cleaning Supplies</h1>
<ul>
<li>
<a class="" href="supplies/Broom">
Broom
</a>
</li>
<li>
<!-- Note the selected element -->
<a class="selected" href="supplies/mop">
mop
</a>
</li>
<li>
<a class="" href="supplies/Hammer">
Hammer
</a>
</li>
</ul>
This view was rendered using the following JavaScript code:
// modified data structure so that array of supplies contains objects
// with the name of the item and whether or not it's selected.
data = {
"title":"Cleaning Supplies",
"supplies":[
{
"value":"Broom",
"selected":""
},
{
"value":"mop",
"selected":"selected"
},
{
"value":"Hammer",
"selected":""
}
]
};
// pass data into template and render
var html = new EJS({url: 'cleaning.ejs'}).render(data);
// add HTML to the DOM using a <div id="container"></div> wrapper.
document.getElementById("container").innerHTML = html;
As you can see, supplies[i].selected applies the selected class to the element that was marked as selected in the data structure. I modified the href value so that it accessed the object in the ith array item instead of the value of the array itself.
Now, when the selected item is modified, we simply modify the data variable, re-render the EJS template, and add it to the DOM.
With this CSS in the head of your HTML document, you'll see something similar to what's displayed below:
<style>
.selected { color:red; }
</style>
Why JavaScript in the template doesn't run:
The method that you're attempting to use to manipulate JavaScript values or use JavaScript values inside the EJS template won't work. This has to do mainly with the context of when the JavaScript is executed.
You're right to think that the EJS templates are compiled on the client-side. However, the JavaScript in the view helpers are executed independent of the JavaScript in the Global context. From looking at the ejs.js source code, it appears as if eval is used in the process.
Additionally, EJS returns the rendered HTML as a string, and the documentation for EJS instructs us to inject that rendered template string into the DOM using innerHTML:
document.getElementById("container").innerHTML = html;
No matter what view technology you're using, one of the fundamental truths of some browsers when it comes to JavaScript is this: Some browsers may not evaluate <script> blocks added to the DOM using innerHTML.
In other words, in my test template, when I tried adding a script tag to output the selected value to the console, I could see that the script block was added, but due to the way innerHTML works, it was not executed:
Example Template Demonstrates JavaScript won't run if added using innerHTML:
<h1><%= title %></h1>
<ul>
<% for(var i=0; i<supplies.length; i++) { %>
<span id="selected"></span><script>console.info('test <%= i %> = <%= supplies[i] %>');</script>
<li>
<a href='supplies/<%= supplies[i] %>'>
<%= supplies[i] %>
</a>
</li>
<% } %>
</ul>
Rendered HTML:
As you can see below, the console.log statements are present in the HTML. However, when added using innerHTML, they will not fire.
The approach to take with view technologies is to limit their use to just that, rendering the view. Keep your logic in the "regular JavaScript".
<h1>Cleaning Supplies</h1>
<ul>
<span id="selected"></span><script>console.info('test 0 = Brrom');</script>
<li>
<a href='supplies/Brrom'>
Brrom
</a>
</li>
<span id="selected"></span><script>console.info('test 1 = mop');</script>
<li>
<a href='supplies/mop'>
mop
</a>
</li>
<span id="selected"></span><script>console.info('test 2 = Hammer');</script>
<li>
<a href='supplies/Hammer'>
Hammer
</a>
</li>
</ul>
More examples and documentation can be found on EJS Templates on the Google Code Embedded JavaScript site.

DOM tree based JavaScript template engines

I am looking for a new Javascript template engine to replace old jQuery Template for my client side templating needs.
I'd prefer approach where the template engine deals with DOM trees instead of text strings and later dumps the content of the cooked string into innerHTML. This is better performance wise and I find DOM manipulation more proper way of constructing more of DOM tree.
What options I do have for Javascript template engine which would directly create DOM trees instead of being text based engines? I like Mustache.js's logicless approach, but it seems to operate on strings only. Native jQuery integration would also be a nice feature.
Transparency:
https://github.com/leonidas/transparency/
PURE:
http://beebole.com/pure/documentation/
Plates
https://github.com/flatiron/plates
Why all this:
http://blog.nodejitsu.com/micro-templates-are-dead
Distal
http://code.google.com/p/distal
soma-template is a new one.
Pure DOM manipulation, a lot of features, natural syntax, fully extensible with other libraries such as underscore.string, function calls with parameters, helpers, watchers. Capability to update only some nodes if needed, templates inside the DOM itself.
http://soundstep.github.com/soma-template/
This is good comparison about 7 famous JS template engine: http://blog.stevensanderson.com/2012/08/01/rich-javascript-applications-the-seven-frameworks-throne-of-js-2012/
I've recently created DOM templating engine inspired by PURE and Transparency.
It supports loops, conditions and much more.
Take a look at doc: http://code.meta-platform.com/metajs/components/template/
Don't be affraid that MetaJS is big library, templating lib can be used standalone.
Short example:
HTML:
<div id="tpl">
<ul id="tasks">
<li>
<span class="task"></span>
<span class="due-date"></span>
</li>
</ul>
</div>
Define template:
var tpl = Meta.Template(document.getElementById('tpl'), {
"ul#tasks li": $__repeat("tasks", {
".task": "task",
".due-date": $__date("d. m. Y", "due_date"),
"#": $__attrIf("completed", "complete")
})
});
Render template:
tpl({
tasks: [
{
task: "Write concept",
due_date: new Date(2015, 3, 22, 0, 0, 0, 0),
complete: true
}, {
task: "Consult with customer",
due_date: new Date(2015, 3, 25, 0, 0, 0, 0),
complete: false
}
]
});
Result:
<div id="tpl">
<ul id="tasks">
<li>
<span class="task" completed>Write concept</span>
<span class="due-date">22. 3. 2015</span>
</li>
<li>
<span class="task">Consult with customer</span>
<span class="due-date">25. 3. 2015</span>
</li>
</ul>
</div>
Take a look at Monkberry DOM template engine.
It is really small (just 1,5kB gzipped), dependency free library, server compiling, one-way data binding, and it's dramatically fast!
Here example of template and generated code:
<div>
<h1>{{ title }}</h1>
<p>
{{ text }}
</p>
</div>
Will generate:
var div = document.createElement('div');
var h1 = document.createElement('h1');
var p = document.createElement('p');
div.appendChild(h1);
div.appendChild(p);
...
view.update = function (data) {
h1.textContent = data.title;
p.textContent = data.text;
};
Monkberry supports if, for and custom tags. And has a lot of rendering optimizations.
Templates can be rendered on server with webpack, browserify or cli.
monkberry.js.org
dna.js is a templating engine that clones DOM elements (https://dnajs.org).
Example template for a book:
<h1>Featured Books</h1>
<div id=book class=dna-template>
<div>Title: <span>{{title}}</span></div>
<div>Author: <cite>{{author}}</cite></div>
</div>
Call to insert a copy of the template into the DOM:
dna.clone('book', { title: 'The DOM', author: 'Jan' });
Resulting HTML:
<h1>Featured Books</h1>
<div class=book>
<div>Title: <span>The DOM</span></div>
<div>Author: <cite>Jan</cite></div>
</div>
Fiddle with a sample "To-Do Application":
https://jsfiddle.net/uLrc7kmp
dna.js was created precisely to avoid constructing and passing around string templates (I'm the project maintainer).
Free MIT-licensed jQuery DNA Template with superpowers (you can re-apply the changed data to the same HTML structure to update UI on any data change...)
https://github.com/webdevelopers-eu/jquery-dna-template/
What sort of sugar are you looking for? The raw DOM api always worked fine for me. If you are really adopting this idea that the templating engines are no good in terms of performance, don't use innerHTML if you want to efficiently build up a DOM tree. What I tend to do is just create elements manually using document.createElement. My templates are created by writing helper functions that create collection of nodes and populate them with the data by setting the .innerText property. I can then cache the references to nodes which I wish to refer to frequently so that I don't have to traverse the DOM tree to find these nodes again.

Categories

Resources