It was a month ago when I found about Jaml, an excellent small Javascript library created by Ed Spencer which allows generating HTML code using very nice and clean templates. At work we use Mootools in most of our projects, so I thought it would be nice to improve Jaml by taking advantage of the power of Mootools. And this is the result.
Mooml: Mootools markup language
What is Mooml? Mooml is a templating system that allows generating HTML snippets very easily, without all the ugly nested “new Element” calls or string concatenation. With Mooml you can save templates for later use or evaluate snippets on the fly.
Download: http://mootools.net/forge/p/mooml
Source: http://github.com/eneko/mooml
Let’s see an example
Required HTML:
-
<div class="node">
-
<h2>Node Title</h2>
-
<p>Node body here.</p>
-
</div>
Node title and body are variables, so let’s save them on a Json format for now.
-
var data = {
-
title: 'Node Title',
-
body: 'Node body here'
-
}
Using Mootools “new Element” we would have to code something like this:
-
var el = new Element('div', {'class': 'node'}).adopt([
-
new Element('h2', {text: data.title}),
-
new Element('p', {text: data.body})
-
]);
That code is much better than plain Javascript, but still messy. We could use the new Elements.from included in Mootools More 1.2.4, but that would require us writing the html as a string, which rapidly gets messy concatenating variables, etc.
-
var el = Elements.from('<div class="node"><h2>' + data.title + '</h2><p>' + data.body + '</p></div>');
With Mooml…
With Mooml, we can create a template for that piece of code, and reuse it later as many times as we need:
-
Mooml.register('node', function(data) {
-
div({'class': 'node'},
-
h2(data.title),
-
p(data.body)
-
)
-
});
-
document.body.grab(Mooml.render('node', data));
We can also execute the template directly as a function:
-
document.body.grab(Mooml.templates.node(data));
Finally, with Mooml we can evaluate templates on the fly, without having to store them for later:
-
document.body.grab(Mooml.evaluate(function(data) {
-
div({'class': 'node'},
-
h2(data.title),
-
p(data.body)
-
)
-
}, data));
More examples soon :)
Hello there, I’ve started to use your library for our project. It’s really awesome and makes my life much simpler, however there’s one thing that would really benefit a lot of people, if it was supported by the library. Namely, the back-referencing the DOM elements when they are created.
If i create a complex UI piece with mooml I either have to create all elements that I need to reference in advance, and put them into the template, or look them up after the template was rendered.
First method breaks the flow, and second one is a waster of CPU cycles, since it has to do more unnecessary DOM traversals.
So if there was a simple way to back-reference those elements that’d be pretty awesome.
Other thing I’ve added for my project is this:
Moomlable =
{
templates: {},
register : function(name, code)
{
this.templates[name] = function(data)
{
return Mooml.evaluate(code, data);
}.bind(this);
},
render: function(name, data)
{
return this.templates[name](data);
}
}
This way I can make my UI classes say Extends: Moomlable, so they have their local template storage.
Hi Vasili, thanks for your feedback.
Yeah, I agree with you. Creating DOM elements from JS is a pain, specially if you need to create complex blocks of elements and worst if you have to reference those elements later on the code.
One thing that Mooml already does is allow you to set events to those elements directly in your template, so maybe that would prevent you from having to reference them later on.
But yeah, there is no direct way of returning references to all elements created. Maybe it would be cool to return a Hash of id’s of the elements rendered linked to the elements themselves. That would be useful, right? Let me know if you have any ideas.
by the way, I like the Class mixin, very interesting :)
returning a hash would prevent from using stuff like .adopt(this.render()).
Maybe return such hash by reference, passed into the render as third argument.
Maybe there can be a special property for each element so you can do something like this:
this.register("template", function()
{
div({'reference':'mydiv'},
div({'reference':'inner'})
)
});
var ref;
el = this.render("template",[],ref);
ref.mydiv.setStyle(...);
ref.inner.setStyle(...);
and if render returns an array of elements, then ref could contain array of hashes.
Another thing i’ve noticed is that when I register the templates, and want to bind class methods to events – i have to do following
this.register("template", function()
{
div({events: click:function(){ ... }.bind(params.self))
});
this.render("template". {self: this});
and if i have to call a template render within another template render it gets even uglier… (for instance if i need to render something in a loop and paste that into outer template. I know that mooml was not meant to be like smarty or something, with loops and whatnot, but passing reference to instance’s this field gets tricky.
I’ve decided to fork it on GitHub to add some of the features I’m looking for…
Hi Vasili, thanks for the ideas. They are really interesting. Sorry I have been so busy lately, I haven’t had time to reply you.
Your idea for creating references is nice, although I think there would be issues when rendering nested templates, something we are doing a lot on the project I am working on. I have a new update coming soon that will incorporate some similar ideas to this. I’ll let you know as soon as it is available.
Regarding the bindings on event functions, you can do that in many different ways. For example, we usually bind the function outside of the template like this:
var options = {
myClickEvent: function() {}.bind(this) // this points to the current object
}
Mooml.render('mytemplate', options);
The template would look something like this:
Mooml.register('mytemplate', function(templateOptions) {
div(a({ events: { click: templateOptions.myClickEvent } }, 'link text'));
});
Note that you can add events also using addEvent:
Mooml.register('mytemplate', function(templateOptions) {
div(a('link text').addEvent('click', templateOptions.myClickEvent));
});
Enjoy!
Salam…
I like this informatin
Good post fox