Creating links in Compound is easy with the linkTo
and pathTo
helpers. Using them
will help to make your app more portable and easier to maintain.
Say we have a Users model, and an index page listing all our users. We could create the following link in our view:
<a href="/users/index">Users</a>
While that may work for now, we can also use a helper to create the link for us:
linkTo('Users', pathTo.users)
The benefit of using pathTo
, is that it will handle the adding the path to page for you,
using the routes configuration file (/config/routes.js
).
To see your routes, do the following in the node console:
compound routes
And you will get a list of your current routes in the following format:
users GET /users.:format? users#index`
In the example above, from left-to-right:
users GET
- The path, this is what we would use inpathTo
/users.:format?
- This tells us what the link looks like, and what paramaters it takes. In this example, the format describes what this contoller action responds to (see controllers forrespondsTo
).users#index
- Convention is controller # action, so this example will look for theindex
action in theusers
controller.
linkTo
also takes optional arguments so that you can add classes, id, etc.
Examples:
linkTo('Cancel', pathTo.users, { class: 'btn', id: 'cancel'})
linkTo('Add Another', false, { class: 'add-to-cart', data-item: 'WDGT-3000'})
Date-Remote:
The linkTo
helper also offers a convenient method for sending asynchronous requests back to the server with
only one additional argument:
linkTo('Users index', '/users', { remote: true })
// <a href="/users" data-remote="true" data-jsonp="renderUsers">Users index</a>
In the example above, the third argument ({ remote: true }), adds a data-remote="true" attribute to the a
tag.
Clicking on this link will send an asynchronous GET request to /users. The result will be executed as Javascript.
You can also specify a jsonp parameter to handle the response:
linkTo('Users index', '/users', { remote: true, jsonp: 'renderUsers' })
// <a href="/users" data-remote="true" data-jsonp="renderUsers">Users index</a>
The server will reply with json response { users: [ {}, {}, {} ] }, and this object will be passed as an argument to the renderUsers function (you will need to create this method in your users_controller):
renderUsers({users: [{},{},{}]});
You can also specify an anonymous function in the jsonp param:
{ jsonp: '(function (url) { location.href = url; })' }
If the server sent you "http://google.com/", the following javascript will be evaluated:
(function (url) { location.href = url; })("http://google.com");
####Form Helpers
Forms are easyto create in Compound with the formFor
helper.
formFor
takes two arguments: resource
and params
, and returns a form
object. As an added protection layer,
forms also generate a csrf token
which is verified
in the conroller when the form is submitted.
The form
object has the following availble helpers:
begin
- opening<form>
tagend
- closing<form>
taginput
label
textarea
submit
checkbox
select
Let's see an example of a form:
<% var form = formFor(user, { action: path_to.users }); %>
<%- form.begin() %>
<p>
<%- form.label('name', 'Username') %> <%- form.input('name') %>
</p>
<p>
<%- form.submit('Save') %>
</p>
<%- form.end() %>
Which ouputs:
<form action="/users/1" method="POST">
<input type="hidden" name="_method" value="PUT" />
<input type="hidden" name="authenticity_token" value="RANDOM_TOKEN" />
<p>
<label for="name">Username</label>
<input id="name" name="name" value="Anatoliy" />
</p>
<p>
<input type="submit" value="Save" />
</p>
</form>
Forms can also be created without requiring a resource with formTagBegin
. This is the "light"
version of the formFor
helper which expects only one argument: params
. Use this helper when you
don't have a resource, but still want to be able to use simple method overriding and csrf protection
tokens.
An example:
<%- formTagBegin({ action: path_to.users }); %>
<%- labelTag('First name', { name: 'name'}) %>
<%- inputTag('name', {value: 'Sascha'}) %>
<%- submitTag('Save') %>
<%- formTagEnd() %>
This will generate:
<form action="/users" method="POST">
<input type="hidden" name="authenticity_token" value="RANDOM_TOKEN" />
<p>
<label for="name">Username</label>
<input id="name" name="name" value="" />
</p>
<p>
<input type="submit" value="Save" />
</p>
</form>
Use the inputTag
helper to create a form in a form where you don't have a resource.
Example:
<%- inputTag({name: 'creditCard', type: 'text', autocomplete: 'off'}) %>
This produces:
<input type="text" name="creditCard" autocomplete="off" />
When you are creating a form for an object that belongs to a resouce (like a user's name), use
form.input
.
<%- form.input('name', {options}) %>
Using the form.input
helper creates the same html markup as inputTag
, but it also add the value
of the resource(in this case User
) passed to form, and specifies it as a value="" html attribute:
<input name="name" value="Sascha" />
<select>
boxes are easy to create if you follow this convention:
If your data is structured like this:
var states = [
...,
{ name: 'California', _id: 3 },
{ name: 'Texas', _id: 47, selected: true },
...
];
...and your form is structured like this:
<%- form.select('state', States, { fieldname: 'name', fieldvalue: '_id' }) %>
...your select list will look like this:
<select name="states">
<option value="3">California</option>
<option value="47" selected="selected">Texas</option>
</select>
Use the labelTag
to create labels for your forms. Just like the inputTag
above, there are two
variations of the labelTag
:
<%- labelTag('Text on label', {'for': 'attachedInput', style: 'font-size: 10px'}) %>
<%- form.label('attachedInput', 'Text on label', {style: 'font-size: 10px'}) %>
will both generate
<label for="attachedInput" style="font-size: 10px">Text on label</label>
When you have a resource, and/or if you are using i18n, use form.label
.
I18N
When using internationalization (I18N), you can change the language of form labels on the fly. So, in the following example:
<%- form.label('name', 'Name', {style: 'font-size: 10px'}) %>
The second argument is omitted if i18n is turned on, and the desired value from locale file is used automatically. For example, if we have a es.yml ( Spanish ):
es:
models:
User:
fields:
name: nombre
...and form looks like:
<% var form = formFor(user); %>
<%- form.label('name') %>
...the result will be:
<label for="name">nombre</label>
i18n and DRY
Instead of manually adding label names, you could just use the locale file to store it for you, and everywhere you used a form, the label would use whatever you added to the locale file.
Create en.yml
with the following:
en:
models:
User:
fields:
name: Name of user
And everywhere you had name
in a form just use:
<%- form.label('name', {options}) %>
// <label for="name">Name of user</label>
Submit tags follow the same conventions as inputTag
and form.input
, but produce a submit button:
<%- submitTag('Submit data') %>
<%- form.submit('Submit data', {options}) %>
Let's put all the form helpers together, and create an address form with a select-list for States. Since we are using Twitter Bootstrap, let's also give it some markup to make it look pretty, too.
We are going to assume a couple things:
- You have an
Address
model that has properties that match those of our form - That
states
is populated from a model, and passed from your controller to your view as astates
object - You are using
ejs
as your template engine (this can be easily used withjade
as well)
<% var form= formFor(user, { id: 'nameForm', class: 'form-horizontal'}); %>
<%- form.begin() %>
<legend>Add Address</legend>
<div class="control-group">
<%- form.label( 'line_1', 'Address Line One', { 'class': 'control-label'}) %>
<div class="controls">
<%- form.input( 'line_1', { 'placeholder': '14000 Morris Ln', 'class': 'span4' }) %>
</div>
</div>
<div class="control-group">
<%- form.label( 'line_2', 'Address Line Two', { 'class': 'control-label'}) %>
<div class="controls">
<%- form.input( 'line_2', { 'placeholder': 'West Tower Plaza', 'class': 'span4' }) %>
</div>
</div>
<div class="control-group">
<%- form.label( 'city', 'City', { 'class': 'control-label'}) %>
<div class="controls">
<%- form.input( 'city', { 'placeholder': 'Los Angeles', 'class': 'span4' }) %>
</div>
</div>
<div class="control-group">
<%- form.label( 'states', 'State', { 'class': 'control-label'}) %>
<div class="controls">
<%- form.select('state', States, { fieldname: 'name', fieldvalue: '_id' }) %>
</div>
</div>
<div class="control-group">
<%- form.label( 'county', 'County', { 'class': 'control-label'}) %>
<div class="controls">
<%- form.input( 'county', { 'placeholder': 'Jefferson', 'class': 'span4' }) %>
</div>
</div>
<div class="control-group">
<%- form.label( 'postal_code', 'Zip Code', { 'class': 'control-label'}) %>
<div class="controls">
<%- form.input( 'postal_code', { 'placeholder': '12345', 'class': 'span1' }) %>
</div>
</div>
<div class="form-actions">
<%- form.submit( '<i class="icon-ok icon-white"></i>', { class: 'btn btn-primary' }) %>
<%- linkTo( 'Cancel', pathTo.addresses, { class: 'btn cancel' }) %>
</div>
<%- form.end() %>
Calling CSS and JavaScript files in your views are easy.
For CSS:
stylesheet_link_tag('bootstrap', 'application', '...')
and for JavaScript:
javascript_include_tag('application', 'date-picker', '...')
You do not need to include the file extension, because Compound will add it for you. All paths are relative to the /public/stylesheets and /public/javascripts directories, respectively.
If you want to include an external CSS or JavaScript, you can use the following format:
javascript_include_tag('//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js')
In the example above, the preceding http or https was ommitted, since Google's CDN provides both SSL and non-SSL versions of the file. Your browser will append the appropriate prefix.
[ coming soon ]
You can create your own custom helpers to perform simple tasks for your view's content.
Helpers are found in the app/helpers
directory, and they are named in following convention:
controller_helper.js
Where controller is the name of the controller (and therefore the directory where the views reside).
If you want a controller to affect the entire application, add it to the application_helper.js file.
Let's create a helper to format a date:
module.exports = {
...
formatDate: function (date) {
return date.toUTCString();
},
...
};
Assuming that we have a date
varaiable available to our view, we can use our custom helper in view
to format it like this:
Created on: <%- formatDate(date) %>
##Authors Daniel Lochrie