generating HTML with javascript    2013-01-29

javascript hoe.js

I often hear people saying something like "HTML must be created using a template engine, anything else is a bad practice". I will try to demystify this dogma...

HTML

HTML is a text document. But browsers don't handle the text directly. It is first converted to DOM (document object model). So HTML is more like a declarative way to describe a user interface program.

This is a very important distinction, one is text the other is an object in a system.

Most components (HTML tags) simply describe how to display some data but other are actual UI components. For example a link (a tag) has an associated interaction with the user, but being part of the standard the browser already knows how to handle it. You don't need to write code for it, you just need to declare its href...

server-side templates

At some point the web moved from static HTML pages to dynamically generated HTML by the server side. After some time people realized that mixing text generation in the code could get quite messy. So they started using template engines.

A template engines combine a template with some data source to create a HTML text document.

Nowadays it is pretty much an established consensus that when generating HTML on the servers-side you should use a template engine, and I agree that's the right thing to do.

javascript templates

As the web evolved the code started to move from server to client. And the templates came together...

But wait. The server has no access to the DOM it can only create HTML text. Javascript has direct access to the DOM!

Should we always programmatically write some text in a declarative interface to create some UI components? Why not directly create these UI objects?

comparison

Lets compare different ways to modify the DOM using javascript to generate the content <div>Hello there</div>.

1) using DOM API

var myDiv = document.createElement("div");
var myContent = document.createTextNode("Hello there!");
myDiv.appendChild(myContent);
document.body.appendChild(myDiv);

Note this is not writing HTML text (although sometimes we may call it this way). It is manipulating the DOM directly. Oh, yes, it sucks :)

2) using jQuery

$('body').append($('<div>Hello there!</div>'));

That's funny, even that we are manipulating the DOM directly you must write HTML markup.

3) another DOM API (hoe.js)

$('body').append(div('Hello there!'));

That looks more reasonable.

4) using templates (Handlebars)

template = Handlebars.compile("<div>{{content}}</div>")
$('body').append(template({content: 'Hello there!'}));

This example is too contrived, normally you would put the template in a script tag.

declarative vs imperative

So which one is better? The answer is, of course, it depends...

Declarative is good for data. If you are just laying out the structure for some data, and using some existing UI component (HTML-tag). Using a template you will get a much more readable code.

example (handlebars):

<ul class="people_list">
  {{#each people}}
  <li>{{this}}</li>
  {{/each}}
</ul>

Declarative is also good if you are just using some custom component. In the example bellow a framework/library would look for elements with a class attribute indicating the widget to be used and the data- attributes to configure it.

<ul class="widget-XYZ" data-opt-a="5">
   some content
</ul>

Template has a big disadvantage, it produces text and loses the direct access to the DOM that is available from javascript.

When the web "moved" from server to the client the default UI elements provided by HTML were not enough to provide a rich user experience. So developers often need to create new UI components in javascript.

If you are creating a new UI component where you need to specify the behavior for a click and other actions you must manipulate the object in the DOM because you need to attach events.

example (hoe.js):

var myComponent = div('click me');
myComponent.click(function(){alert('Hello there!')});

So in one way or another you will have to get back a reference to the created object. If you use a template this is usually done by a CSS selector.

Using a CSS selector is bad for many reasons. It might be slow (need to search in the DOM). The code is error prone because it depends on attribute values that are far away from the code. The selectors have two meanings (styling with CSS and reference in javascript) that can gets confusing.

Another choice would be that your template/HTML reference the javascript code. For very simple things this was always possible using onclick and other attributes for events. Some frameworks provide more sophisticated mechanism like associating with a "controller", etc. The problem with this approach is that it doesn't scale as your component evolves. When it gets more complex you try to stuff more logic in the template. When your template can't handle it anymore you need to throw away your template and re-write it in javascript...

conclusion

If you are creating an UI component, manipulating the DOM from javascript is probably your best option.

If you are just filling some data and using existing components, templates are probably better for you.

Comments

mongodb setup for deployd on heroku    2013-01-27

node.js mongodb

I am using deployd to prototype an applicaton. It's been great, really helped me focus on what matters for the prototype.

Today I reached the first milestone and decided to deploy it in heroku.

I had a small problem because deployd uses an object to config mongodb, but heroku provides only a URL to mongodb server... So here is the script I am using to run deployd on heroku:

var deployd = require('deployd');

// on heroku must use port from env
var port = process.env.PORT || 3000;

var url = require('url');
var db_url = url.parse(
    process.env.MONGOHQ_URL || "mongodb://:@localhost:27017/my_db_name");

var options = {
    port: port,
    db: {
        "host": db_url.hostname,
        "port": parseInt(db_url.port),
        "name": db_url.pathname.slice(1),
        "credentials": {
            "username": db_url.auth.split(':')[0],
            "password": db_url.auth.split(':')[1]
        }
    }
};

var server = deployd(options);
server.listen();

server.on('listening', function() {
  console.log("Server is listening on " + port);
});

server.on('error', function(err) {
  console.error(err);
  process.nextTick(function() { // Give the server a chance to return an error
    process.exit();
  });
});

Comments

strace & build-tools    2013-01-11

python doit

strace is a utility to monitor system calls. I.e. it can report all files open by a process.

fabricate.py is a build-tool based on strace. The idea is pretty cool. It will execute all commands through strace. Than it can automatically figure out the dependencie and targets by looking at the strace output. So you don't need to explicitly specifying the dependencies and targets.

But these approach has a few problems:

  • strace will slow down the execution

(not only because of strace itself but also becaue it will have to parse strace output)

  • not always correct. i.e. run('cp', '-r', 'src', 'out')

If you just add a file to the src it can not figure out that a "dependency" was added.

  • confuse targets & dependencies

It checks the mode files were open, if open in write mode it is target. The problem is that some programs have their own cache system, so a target might be taken as a dependency.

Where these limitations are a problem or not depends on your use-case...

doit & strace

doit takes a very different approach to dependency handling. All dependencies must be explicitly defined.

doit doesn't support any kind of implicit dependency but it now comes with a strace command that lets you easily check which files are being used.

The idea is that you can use this feature while developing your tasks and make sure you are setting the dependencies correctly.

Example:

def task_o():
    return {'actions': ['cp abc abc2', 'touch xyz']}
$ doit strace -f traceme.py o
.  o
.  strace_report
R /xxx/abc2
R /xxx/abc
W /xxx/abc2
W /xxx/xyz

For more details check the docs.

Comments


Contents © 2012 schettino72 - Powered by Nikola