hamlet.coffe

Take full advantage of CoffeeScript (GitHub: jashkenas/coffeescript, License: MIT) in your templates. hamlet (GitHub: inductor-labs/hamlet, License: MIT) doesn’t use a crippled templating language and supports embedding arbitrary CoffeeScript expressions. Write expressive, intuitive templates that can be understood at a glance.

Compared to Backbone (GitHub: jashkenas/backbone, License: MIT)hamlet is much less verbose. Using hamlet, you’ll never have to set up handlers or manually sync state between your model and the DOM. Eliminating this entire class of application code reduces complexity and improves maintainability.

npm install hamlet.coffee

Usage

The template language draws on the original old school Ruby HAML template language (Hamlet… get it?). For those of you that have experience with HAML or JADE (GitHub: visionmedia/jade, License: MIT), you will feel right at home.

Here’s what a typical hamlet template looks like:

-th = ["Category", "Product", "Price", "Quantity", "Subtotal", ""]
%table
  %thead
    %tr
      - each th, (name) ->
        %th= name
  %tbody
    - each @lines, ->
      %tr
        %td.category
          %select(options=@categories value=@selectedCategory)
        %td.product
          %select(options=@products value=@selectedProduct)
        %td.price= @price
        %td.quantity
          %input(type="number" value=@quantity)
        %td.subtotal= @formattedSubtotal
        %td.remove
          %a(href="#" @click) Remove
%hr
.sum
  Total value:
  %span.total= @formattedTotal
%button(click=@addLine) Add product
%button(click=@submitOrder) Submit order

You can see that it flows really well with CoffeeScript. In addition to basic templating functionality hamlet also provides browser specific features like basic data binding and event handler wire up.

The context object for the template above:

nullCategory = {name: "Select...", value: -1, products: []}
nullProduct = {name: "Select...", value: -1, price: 0}

categories = Observable [nullCategory].concat data.categories

Line = ->
  self =
    categories: categories
    selectedCategory: Observable(categories()[0])
    quantity: Observable(1)
    subtotal: Observable(0)
    price: ->
      @selectedProduct().price
    products: ->
      [nullProduct].concat(@selectedCategory().products)
    selectedProduct: Observable(nullProduct)
    subtotal: ->
      @price() * @quantity()
    formattedSubtotal: ->
      "$" + @subtotal().toFixed(2)
    toJSON: ->
      if @selectedProduct()
        { productName: @selectedProduct(), quantity: @quantity() }
    click: (e) ->
      e.preventDefault()
      lines.remove(self)

  self.selectedCategory.observe (category) ->
    self.selectedProduct(nullProduct)

  self

lines = Observable([Line()])

model =
  addLine: ->
    lines.push(Line())
  lines: lines
  submitOrder: ->
    json = JSON.stringify lines.map (l) ->
      l.toJSON()

    alert("Could now send this to server: " + json)
  total: ->
    sum = lines.reduce (total, line) ->
      total + line.subtotal()
    , 0
  formattedTotal: ->
    "$" + @total().toFixed(2)

What else?

You can play with interactive examples on the hamlet.coffee site and see feature equivalent code for a few other frameworks.

Post navigation

Leave a comment

Leave a Reply

Your email address will not be published. Required fields are marked *