
Hamlet – Simple and powerful reactive templating - Yahivin
http://hamlet.coffee/
======
chenglou
Comparing your CoffeeScript example against a vanilla JS React example seems
cheap. here's the front page React example in CS:

    
    
        converter = new Showdown.converter()
    
        MarkdownEditor = React.createClass
          getInitialState: () -> value: 'Type some *markdown* here!'
          handleChange: (e) -> @setState(value: e.target.value)
          render: () ->
            d = React.DOM
            d.div(
              d.h3(null, 'Input'),
              d.textarea(onChange: @handleChange, value: @state.value),
              d.h3(null, 'Output'),
              d.div(dangerouslySetInnerHTML: __html: converter.makeHtml(@state.value))
            )
    
        React.renderComponent MarkdownEditor(), document.querySelector('.container')
    

Showing a LOC comparison (and not even from the same dialect of a language)
isn't a good proof for what you're trying to demonstrate. Clarity, simplicity,
and debuggability all count. Removing a few extra lines of (non-boilerplate)
code compared to React doesn't make the library simpler to work with.

~~~
mdiebolt
I thought about that when constructing the demo but opted to use the JSX /
plain JavaScript version because that's what's displayed on the React
homepage.

We have a strong preference for Haml and CoffeeScript dialects so that's how
we present our demos. I built jsfiddles based on how each framework presented
their own product.

~~~
chenglou
It is indeed displayed on the front page, in vanilla JS, but that doesn't
change the fact that it's not a very accurate comparison, providing that the
point you're trying to prove is LOC. The React version also showcased
attributes such as `className`, which Hamlet didn't. Nothing against
CoffeeScript; just picking on the comparison itself.

Also, React is a view library, not a full-stack framework. The fact that it's
put on the same level as other real JS frameworks is an acknowledgment of its
paradigm's power and usefulness. So I guess people viewing it as a framework
is a misleading but very telling phenomenon.

That being said, good luck with the project!

------
Tyr42
Oh, I thought this was the already existing Hamlet language.

Of which there are at least two.

[http://www.yesodweb.com/book/shakespearean-
templates](http://www.yesodweb.com/book/shakespearean-templates)

[https://github.com/gregwebs/hamlet.rb](https://github.com/gregwebs/hamlet.rb)

It does look pretty cool though.

~~~
Grothendieck
Actually, at least one: the Ruby version is just a port from Haskell and has
the same syntax.

~~~
Tyr42
Oh, shoot, I googled for the Haskell one, and saw the other one on the way. I
should have read about two more lines, and I would have got it.

In my defence, I always thought the main feature of Hamlet is the static
checking of everything, and solving the escaping problem (at compile time!),
so I didn't even entertain the idea that someone would port it to Ruby.

------
chriswarbo
An unfortunate choice of name. When I saw the title, I thought it was on about
[https://hackage.haskell.org/package/hamlet](https://hackage.haskell.org/package/hamlet)

~~~
Yahivin
It's a popular play!

~~~
dllthomas
And contains H, T, M, and L.

------
bradgessler
We use hamlc and Backbone.js in our stack. This lib looks like it will
simplify a lot of that.

I have a few questions:

1\. Its interesting to see JS events specified in the template (e.g.
`%a(onclick=@doSomething)`). Is there a way to specify that in the JS/model?

2\. Does "Observable" mean that the value is updated when the model changes,
when the DOM changes, or both? Could all of the attributes of the object
passed into the template be observable by default or would that incur a
significant performance penalty?

~~~
Yahivin
For point one, it is specified in the model. @doSomething would invoke the
model's doSomething function. The template just names it.

As to point two, observable provides a bi-directional binding so that changes
to the value are reflected in the DOM and changes in the DOM are reflected in
the model.

The observable interface is essentially a jQuery style getter/setter method
that allows for observers to be notified of changes.

~~~
klibertp
When is the DOM updated? Are the changes performed as soon as they are
observed or are they queued? Does Hamlet implement some kind of "virtual DOM"
for diffing or does it sync DOM and model directly?

Looks very nice, just this weekend I looked at some frontend frameworks,
including Vue, Mithrill, Ractive and React and found some issues with them
all. Hamlet would fit my use case very well, I think. One thing I'd like is
more technical details on the main page - not how it "looks like" compared to
other frameworks, but how it does its thing (compared to other frameworks).

~~~
Yahivin
The current implementation performs changes immediately. There is no virtual
DOM, as soon as a change occurs the model is update and vice versa.

The Observable component invokes the callback to all listeners immediately
when its value is changed. I find this makes testing and debugging somewhat
easier than async callbacks or nextTick.

~~~
bradgessler
How does observable wrap values? I see a few things:

    
    
      list = Observable []
      list.push 'blah'
    

and

    
    
      isOrange = Observable false
      isOrange = true // This obviously doesn't work this way...
    

Do you poll the value? Or do you check the type of the value and wrap/proxy
methods that would normally change the value?

~~~
Yahivin
When Observable is declared with a type (especially an array) it sets up some
wrapped methods to simplify working with it.

The wrappers also set up automatic dependency resolution so you can do cool
things like:

    
    
        list = Observable []
    
        reversed = Observable ->
          list.map reverse
    
        list.push 'blah'
    
        reversed() # => ['halb']
    

There's no polling, the observable notifies all observers immediately when the
value changes.

~~~
bradgessler
What about simple types, like the `false` value example above? Is there a
`set` and `get` method?

~~~
mdiebolt
You'd use a jQuery style setter.

    
    
      isOrange(false)

------
peterhunt
> Avoid working with over-engineered frameworks without sacrificing a great
> interactive experience

This is a pretty lame claim seeing as text fields are busted in Hamlet. [1]

This is another example of a "lightweight" library that hasn't hit any of the
hard problems yet. It's fine if you make this your personal project to learn
from, but trying to convince people to bet their projects on unproven
technology is pretty disingenuous.

[1] Inserting characters does not work in the second example at
[http://hamlet.coffee/garden/](http://hamlet.coffee/garden/)

~~~
CanSpice
All of those examples work for me with Chrome 35.

One thing I would love on HN is the reluctance to say something is "lame"
because one example doesn't work for one person on one browser. Instead of
being dismissive, why not be constructive? Or if you can't even do that, just
don't post?

~~~
peterhunt
If you look at my posting history you'll see that I tend to agree with your
sentiment. Too much negativity on HN and I combat it where I can.

But "lightweight" JS fetishism at the expense of correctness (and the
arrogance that comes with it!) is an epidemic in the frontend web business
today. I think it's important to highlight this when it happens so we can stop
making crappy web apps and start to actually deliver reasonable experiences
when compared to native.

Also here is a video of the bug:
[http://www.petehunt.net/hamlet.mov](http://www.petehunt.net/hamlet.mov)

~~~
jonahx
I see the bug too on Chrome 35 on a Mac.

That said, and with lots of respect for your work on React Pete, and also
acknowledging that React is probably the simplest of the big frameworks, the
love of minimalism and simplicity comes from a very real need and attempts to
fulfill that need shouldn't be dismissed as toy projects. From what I can
tell, Mithril, for example, is anything but that.

I think people really really like being able to jump into something that can
be useful and played with based on a few examples, and to model things with
POJOs. I agree correctness shouldn't be sacrificed, but it doesn't have to be.

~~~
peterhunt
Totally agree. If someone comes up with a simpler way to do it and proves it
out on a few projects of real complexity, then I think it's great.

My complaint is you see a lot of upstart projects pulling mindshare when the
reason they're simple is either a matter of personal preference or a lack of
essential complexity. Hamlet was an easy target because cursor position
management is a great example of essential complexity and they made pretty
aggro claims on their site about how over-engineered everything else is.

------
cfitzhugh
I've really enjoyed ractive. Simple to learn, with mustaches, and it has
worked really well for our project. No extra compile steps. Figured I'd share
since I see alot of buzz around React and other things, and preferred
ractive.js when I researched it a little bit ago.

~~~
Yahivin
Wow, I hadn't heard of Ractive before. It looks quite similar in approach and
also really cool.

------
zeekay
Would be extremely appealing if it borrowed more inspiration from Jade than
Haml.

~~~
Yahivin
I actually agree! I started with haml because it's what I knew best at the
time.

The parser is separate from the compiler and the runtime, so it should be
simple enough to add a jadelet, anglet, or any other simple style of adapter.

If there is a lot of interest in a jade focused style or haml is a turn off
for many people then it will become a priority for us.

~~~
lobo_tuerto
You should checkout slim too, it's just a better haml.

[http://slim-lang.com/](http://slim-lang.com/)

------
smrtinsert
Reactivity is becoming an imperative. The browser of the future will let us
code in a reactive language instead of forcing non reactive html or js on us.
I'd love to see something simple for other languages and platforms as well.

I love that it seems to not be tied to node out of the box as well.

~~~
Yahivin
Yeah, it's only taken us 50 years :)

One of our goals was for Hamlet to be suitable for really small and simple web
apps, without any big framework or ecosystem. There's still a lot of work for
us to do on the ease of getting started (both with or without node) so if you
run into trouble or have any comments let us know.

~~~
smrtinsert
Will do, and great job.

------
Smudge
Got this JS alert: "This website abuses rawgit.com. You should complain to its
owner."

~~~
Yahivin
It looks like one of the JS fiddle examples we found for our demos wasn't up
to our regular standards. We're looking into it.

------
mquandalle
This looks to be a great declarative/reactive template engine. I've been
working mostly with the Meteor Blaze template engine the last few months. Both
of them use a "normal" template language for writing views and (potentially)
let you choose if you prefer writing your templates in Handlebars, Jade, or
Haml [0], which I find far more easy to use than React JSX format. I think
Blaze beats Hamlet on the runtime rendering engine.

First, Blaze does not require to set a root element in a template, which could
be a source of bugs with Hamlet because for instance the `each` child is a
template, here is a snippet of problematic example from the Hamlet README:

    
    
      - each @items, ->
        .first
        .second
    

This works perfectly fine in Blaze. IIRC Blaze uses comments node on the DOM
that are never rendered in browsers in order to define some "domrange" that
keep track of n children in a single parent group.

The second runtime issue in Hamlet appears when a third-party library directly
modifies the DOM, without telling the template engine. Basically the
modification will be erased on the next template redraw which make this system
incompatible with all jQuery plugins for instance. Blaze has "fined grained
DOM updates" which mean that the modification of a single element in a
template does not require to touch any other node in the DOM. For instance if
you have a each loop of inputs, and the user start to enter some data in one
input field, and for some reason the template is redrawn the text will stay in
the input with Blaze, but will be erased with Hamlet.

Blaze also support reactive SVG (I'm not sure if Hamlet supports it but I
haven't seen any particular mention in the code).

I think all of these features can be implemented in Hamlet drawing on Blaze
and ReactJS runtimes.

Nevertheless I find the Javascript model declaration cleaner in Hamlet than in
Blaze or Backbone or React. The only thing I'm not sure about is writing the
js events in the template and not in the model, I actually like having all
events of a given template in a single place but I don't have strong opinion
on this.

[0]: Meteor support Spacebars (which is quite similar to Handlebars) by
default
[https://github.com/meteor/meteor/blob/devel/packages/spaceba...](https://github.com/meteor/meteor/blob/devel/packages/spacebars/README.md),
and there is also a package for jade [https://github.com/mquandalle/meteor-
jade](https://github.com/mquandalle/meteor-jade) (disclaimer: I'm the author).
It also seems that it wouldn't be difficult to support other languages than
Haml for Hamlet.

~~~
Yahivin
That issue about requiring a root item should be solvable in the future, it's
just the current implementation that has limitations. I'm building up the test
suite to specify the behavior and hope to have it working soon.

For the most part jQuery plugins should work fine with Hamlet, so long as one
remembers to update the data in the model rather than arbitrarily throughout
the DOM. It can be a moderate mental shift to go from jQuery style "The DOM is
the data" to the newer Backbone, Knockout, React, Angular, etc style of "The
model is the data" and may not be right for all applications.

Thanks for the comment I'll take a look at Meteor Blaze and see what cool
tricks it has :)

~~~
mquandalle
The problem is that most _current_ jQuery plugins modify the DOM. If someone
want to use a jQuery carousel, the plugin will add arrows buttons and page
indicators in the DOM. Then if the template engine updates one of the carousel
slide, these nodes will be removed and the plugin will be broken.

~~~
Yahivin
My advice for that kind of application would be to have a piecemeal approach.
Use Hamlet for some templates on your page, and jQuery plugins for others. I
hope it's modular enough to use as little or as much as you like.

------
krick
I'm more intrigued by the domain name. Why there even exists 1st level domain
"coffee"?

------
coherentpony
Oh wow, people are actually using these new TLDs.

~~~
mdiebolt
It's pretty awesome to be able to host CoffeeScript OSS projects on a .coffee
TLD

------
jonahx
@Yahivin, I'd love to hear your thoughts on Mithril. Have you used it?

~~~
Yahivin
I hadn't seen Mithrill before, but from checking it out now it looks like it
has a lot in common: small runtime, trying to be as close to plain JS and DOM
as possible, and safety by default.

I'm not sure if it provides as much magic as Hamlet's auto-dependencies and
template syntax, but those do have costs and tradeoffs.

I would like to see an interactive demo on the site so I could get to know it
better by messing around.

------
bmcmaste
Looks really interesting. Any plans for a Rails Gem?

~~~
Yahivin
Yes, we plan to create or assist the creation of drop in solutions for Rails,
Sinatra, and popular node frameworks, priority based on interest, volunteers,
and whoever demands it the loudest.

------
toisanji
its too bad this is made for coffescript, i would have liked to see it for
regular javascript.

~~~
Yahivin
It does work with regular JS though we present our examples in CoffeeScript.

------
notastartup
What's wrong with just using jQuery? what's the rational for using a reactive
solution?

~~~
Yahivin
Say you're building a color picker with three text fields for R, G, B, and a
swatch that displays the resulting color.

You could do it in jQuery, but plumbing all the update code for each input
would be kind of a chore. If you get a new source of input (say from the
network) you'd have to remember to resync everything. To keep it simple you'd
probably have to respond to `something changed` -> `update everything`.
Reactive templates would only update the elements dependent on that change.

With a reactive solution you make a model that contains observable RGB values
and a function to compute the final color. Once you bind that model to your
view everything stays in sync like magic.

