Tower Views

Tower has built in support for every template framework.

Helper Methods

Components

A lot of thought was put into figuring out the conventions behind most of the HTML components we use. Where applicable, you can opt-into ARIA-roles as well. The structures all have sensible defaults, but they can be globally configured no problem.

Tower also includes several "meta-level" helpers, such as:

Layouts

doctype 5
html ->
  head ->
    partial "shared/meta"

  body role: "application", ->
    if hasContentFor "templates"
      yields "templates"

    nav id: "navigation", role: "navigation", ->
      div class: "frame", ->
        partial "shared/navigation"

    header id: "header", role: "banner", ->
      div class: "frame", ->
        #if hasFlash()
        #  renderFlash()
        partial "shared/header"

    section id: "body", role: "main", ->
      div class: "frame", ->
        yields "body"
        aside id: "sidebar", role: "complementary", ->
          if hasContentFor "sidebar"
            yields "sidebar"

    footer id: "footer", role: "contentinfo", ->
      div class: "frame", ->
        partial "shared/footer"

  if hasContentFor "popups"
    aside id: "popups", ->
      yields "popups"

  if hasContentFor "bottom"
    yields "bottom"

Forms

formFor @user, (form) ->
  form.fieldset "Profile", (fields) ->
    fields.field "firstName"
    fields.field "lastName"
    fields.field "email"
  form.fieldset "Address", (fields) ->
    fields.field "lat", as: hidden
    fields.field "lng", as: hidden
    fields.field "street"
    fields.field "city", as: "select", collection: ["CA"]
<form class='form' data-method='post' method='post' novalidate='true' role='form'>
  <fieldset class='fieldset' id='profile'>
    <legend class='legend'>
      <span>Profile</span>
    </legend>
    <ol class='field-list'>
      <li class='field string optional validate'>
        <label class='label' for='active-record-user-first-name-input'>
          <span>First Name</span>
          <abbr class='optional' title='Optional'></abbr>
        </label>
        <input accesskey='f' class='string first-name optional input validate' data-validate='presence' data-validates-presence-message="can't be blank" data-validates-presence='true' id='active-record-user-first-name-input' maxlength='255' name='activeRecordUser[firstName]' type='string' value='Lance' />
        <output class='error'></output>
      </li>
      <li class='field string optional validate'>
        <label class='label' for='active-record-user-last-name-input'>
          <span>Last Name</span>
          <abbr class='optional' title='Optional'></abbr>
        </label>
        <input accesskey='l' class='string last-name optional input validate' data-validate='presence' data-validates-presence-message="can't be blank" data-validates-presence='true' id='active-record-user-last-name-input' maxlength='255' name='activeRecordUser[lastName]' type='string' value='Pollard' />
        <output class='error'></output>
      </li>
      <li class='field email optional validate'>
        <label class='label' for='active-record-user-email-input'>
          <span>Email</span>
          <abbr class='optional' title='Optional'></abbr>
        </label>
        <input accesskey='e' class='email string optional input validate' data-validate='presence' data-validates-presence-message="can't be blank" data-validates-presence='true' id='active-record-user-email-input' maxlength='255' name='activeRecordUser[email]' type='email' value='example@gmail.com' />
        <output class='error'></output>
      </li>
    </ol>
  </fieldset>
  <fieldset class='fieldset' id='address'>
    <legend class='legend'>
      <span>Address</span>
    </legend>
    <ol class='field-list'>
      <li class='field hidden optional'>
        <input accesskey='l' class='hidden lat optional input' id='active-record-user-lat-input' name='activeRecordUser[lat]' type='string' />
      </li>
      <li class='field hidden optional'>
        <input accesskey='l' class='hidden lng optional input' id='active-record-user-lng-input' name='activeRecordUser[lng]' type='string' />
      </li>
      <li class='field string optional'>
        <label class='label' for='active-record-user-street-input'>
          <span>Street</span>
          <abbr class='optional' title='Optional'></abbr>
        </label>
        <input accesskey='s' class='string street optional input' id='active-record-user-street-input' name='activeRecordUser[street]' type='string' />
        <output class='error'></output>
      </li>
      <li class='field select optional'>
        <label class='label' for='active-record-user-city-input'>
          <span>City</span>
          <abbr class='optional' title='Optional'></abbr>
        </label>
        <select accesskey='c' class='select city optional input' id='active-record-user-city-input' name='activeRecordUser[city]'>
          <option value='CA'>CA</option>
        </select>
        <output class='error'></output>
      </li>
    </ol>
  </fieldset>
</form>

Tables

tableFor "users", (t) ->
  t.head ->
    t.row ->
      t.header "name", width: 400, sort: true
      t.header "createdAt", sort: true
  t.body ->
    for user in users
      t.row ->
        t.cell ->
          linkTo(user.name, adminUserPath(user))
        t.cell ->
          time user.createdAt
  t.foot ->
    t.row ->
      t.cell colspan: 2, ->
        render partial: "shared/pagination", locals: {collection: @users}
<table class='data-table' data-for='users' data-url='/admin/users' id='users-table' role='grid' summary='Table for Users'>
  <thead>
    <tr role='row' scope='row'>
      <th abbr='name' aria-sort='none' class='sortable' id='users-header-1-0' role='columnheader' scope='col' width='400px'>
        <a href="/admin/users?sort=name+">Name</a>
      </th>
      <th abbr='createdAt' aria-sort='none' class='sortable' id='users-header-1-1' role='columnheader' scope='col'>
        <a href="/admin/users?sort=createdAt+">Created At</a>
      </th>
    </tr>
  </thead>
  <tbody>
    <tr class='odd' role='row' scope='row'>
      <td headers='users-header-1-0' id='users-cell-1-0' role='gridcell'>
        <a href="/admin/users/2288">Lance Pollard</a>
      </td>
      <td headers='users-header-1-1' id='users-cell-1-1' role='gridcell'>
        <time>5/29/2011 @ 03:44pm</time>
      </td>
    </tr>
    <tr class='even' role='row' scope='row'>
      <td headers='users-header-1-0' id='users-cell-2-0' role='gridcell'>
        <a href="/admin/users/2287">John Smith</a>
      </td>
      <td headers='users-header-1-1' id='users-cell-2-1' role='gridcell'>
        <time>5/29/2011 @ 03:40pm</time>
      </td>
    </tr>
  </tbody>
  <tfoot>
    <tr role='row' scope='row'>
      <td colspan='3' headers='users-header-1-0' id='users-cell-1-0' role='gridcell'>
        <nav class='paginator' role='toolbar'>
          <ul class='goto-pages'>
            <li class='goto-search'>
              <a href="#search" class="search-pages" title="Toggle Advanced Search">&#8981;</a>
            </li>
            <li class='goto-page'>
              <a href="/admin/users?page=1" aria-disabled="true" class="first-page disabled" data-page="1" rel="first" title="Go to the first page">&#8676;</a>
            </li>
            <li class='goto-page'>
              <a href="/admin/users?page=1" aria-disabled="true" class="prev-page disabled" data-page="1" rel="prev" title="Go to page 1">&#8672;</a>
            </li>
            <li aria-valuemax='109' aria-valuemin='1' aria-valuetext='1' class='goto-page current-page' role='spinbutton'>
              <span>Page</span>
              <input class='current-page-input' value='1'>
              <span>of</span>
              <span class='page-count'>109</span>
            </li>
            <li class='goto-page'>
              <a href="/admin/users?page=2" aria-disabled="false" class="next-page yes" data-page="2" rel="next" title="Go to page 2">&#8674;</a>
            </li>
            <li class='goto-page'>
              <a href="/admin/users?page=109" aria-disabled="false" class="last-page yes" data-page="109" rel="last" title="Go to the last page">&#8677;</a>
            </li>
          </ul>
          <output class='record-count'>
            <span>Viewing</span>
            <span class='current-record-range'>1 - 20</span>
            <span>of</span>
            <span class='total-record-count'>2178</span>
          </output>
        </nav>
      </td>
    </tr>
  </tfoot>
</table>

Templates

Automatic Template Refreshing in Browser

When you save a template, you're either saving a template, partial, or layout.

Examples

Coffeekup + Mustache

mustacheEach "posts", ->
  h2 "{{title}}"
  p "{{body}}"
{{posts}}
  <h2>{{title}}</h2>
  <p>{{body}}</p>
{{/posts}}