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 theindexaction in theuserscontroller.
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>taginputlabeltextareasubmitcheckboxselect
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
Addressmodel that has properties that match those of our form - That
statesis populated from a model, and passed from your controller to your view as astatesobject - You are using
ejsas your template engine (this can be easily used withjadeas 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