Tag Archives: handlebars

Coding without coding: announcing the Open PHACTS HTML widgets

What if you could have an element in your web page which is replaced by some HTML depicting some really cool chemistry stuff without having to know a lot about HTML, CSS, Javascript or how the Open PHACTS API works. Well, that’s our intention with the OPS HTML widgets. It’s built using the ideas and templates we designed for the Explorer but without the full Ember JS MVC stack. It’s early days for the library and there are only a couple of widgets available right now. We are also looking at aligning with BioJS.

Just by adding a div with a specific class and a data attribute you can get facts (phacts!) and images for chemical compounds and targets embedded in your page with no need to know anything about the Open PHACTS API.
You need to load the jquery, handlebars, ops.js and ops-html-widgets libraries in your page and then you can either insert the info using a div like


<div id="compound-info-div" style="display: none;"/>

or programatically using some javascript like


var compoundWidget = new Openphacts.CompoundWidget(appUrl, appID, appKey); compoundWidget.infoByURI("http://www.conceptwiki.org/concept/dd758846-1dac-4f0d-a329-06af9a7fa413", "compound-info-div");

You can style the divs and include as much or as little info you want using simple handlebars tags like


<div id="compound-info-div" style="display: none;">
  <div>Preferred Label: {{prefLabel}}</div>
  <div>SMILES: {{smiles}}</div>
  <div>Inchi: {{inchi}}</div>
</div>

The results from one of these compound-info-divs has magically appeared below. Honest.




Ember JS: MVC in the browser. Part 3 – Nested Resources

In part 2 we created templates so that we could view any of our blog posts along with the models and controller actions to support this. We also used ember-data to create a store which uses a FixtureAdapter along with some hardcoded Post fixtures to load our models. A Post with a title is not much use on its own, we also want people to be able to add comments. We will have to extend the post route to add a nested resource. We will also need a new model to represent the nested resource. The code for this part is available here. Open app.js in an editor and change the post route to match the following:

this.resource('post', { path: '/posts/:post_id' }, function() {
  this.resource('comments');
});

This means we will now have a /posts/:post_id/comments route.

Open the index.html page in a browser and examine the routes in the console. Remember our old friend Router.router.recognizer.names? (btw. I noticed in Firefox you may need to use App.Router.router.recognizer.names). You will see that comments route has been added. Lets add a comments model and fixtures. Add the following to app.js

App.Comment = DS.Model.extend({
  post: DS.belongsTo('App.Post'),
  text: DS.attr('string')
});

and add the following within the App.Post model making sure that you have a comma between any lines

comments: DS.hasMany('App.Comment')

Each Post can now have many Comment(s). Add some comment fixtures to app.js:

App.Comment.FIXTURES = [{id:"1", text: "First Comment"}, {id:"2", text: "Second Comment"}, {id:"3", text: "Third Comment"}, {id:"4", text: "Fourth Comment"}, {id:"5", text: "Fifth Comment"}, {id:"6", text: "Sixth Comment"}, {id:"7", text: "Seventh Comment"}, {id:"8", text: "Eighth Comment"}, {id:"9", text: "Ninth Comment"}];

We also need to change the Post fixtures to tell them that they have some comments:

App.Post.FIXTURES=[{ id: "1", title: "First Post", comments: [1, 2, 3] }, { id: "2", title: "Second Post", comments: [4, 5, 6] }, { id: "3", title: "Third Post", comments: [7, 8, 9] }];

Note that we have added a ‘comments’ attribute in each Post fixture with an array of the Comments they have. [1, 2, 3] means that this Post has the Comments with ids 1, 2 & 3. Go to /posts/1/comments/1 to check that they render correctly.

Change the post/index template to the following so that we can see the comments:

<script type="text/x-handlebars" data-template-name="post/index">
  {{title}}
  <ul>
    {{#each comment in comments}}
      <li>{{comment.text}}</li>
    {{/each}}
  </ul>
</script>

Go to /posts/1 and you should see the text for each Comment appearing under the Post title.

We can now add a link to each comment but before this we need to tell the router how to route to them. We want the route to look like /posts/:post_id/comments/:comment_id
To achieve this we need to add the following to the posts resource router definition

this.route('comment', { path: 'comments/:comment_id'});

We will also need a post.comment template in index.html

<script type="text/x-handlebars" data-template-name="post/comment">
  {{text}}
</script>

What if we wanted to add a new comment to a post? We will add a posts/:post_id/comments/new route and templates. Change the comments resource under the post resource in the router to

this.resource('comments', function() {
  this.route('new');
});

Then add a comments outlet template and a ‘new’ one

<script type="text/x-handlebars" data-template-name="comments">
  {{outlet}}
</script>

<script type="text/x-handlebars" data-template-name="comments/new">
  Comments New
</script>

 

If you refresh and look at the available routes we now have comments.new. Go to /posts/1 and click on the New Comment link and you should see the “Comments New” text. Not much use though, we need a text box for the comment and a button to submit. Add the following to the comments/new handlebars template

<form {{action save on='submit'}}>
  {{view Ember.TextArea valueBinding="text" placeholder="Enter comment here"}}
  <button type="submit">Create Comment</button>
</form>

Here we tell the template that when the forms “Create Comment” button is clicked, ie on submit, it should call the “save” action in the CommentsNewController. We need to create that controller. In the TextArea we have bound the “text” value to the controller. Add the following to app.js

App.CommentsNewController=Ember.ObjectController.extend({
  needs: 'post',
  text: null,
  save: function() {
    var post = this.get('controllers.post.content');
    App.Comment.createRecord({ post: post, text: this.get('text') });
    this.get('target').transitionTo('post.index');
  }
});

Firstly we tell this controller that it needs to know about the PostController so that it can tell what post the comment is for. We do this through needs: 'post'. The text: null bit is the value we have bound to the TextArea in the template. The save function is what happens when we click the button. Firstly it finds the post through controllers.post.content (facilitated via the ‘needs’ syntax), the we create a new Comment with the text value. Finally we transition back to the post. this.get('target') gives us an instance of the router. Try adding a new comment and see what happens. Note that since we are using the FixturesAdapter the ids may look a little bit odd.

One final thing we should do is reset the comments TextArea box in the comments/new template when we transition to the route. If we don’t then it will contain the last thing we entered in to it. Add this to app.js

App.CommentsNewRoute=Ember.Route.extend({
  setupController: function(controller, model) {
    controller.set('text', null);
  }
});

setupController is a hook that all routes have and allows you to set the model or modify the controller.

To recap, we created a comments nested resource for post and added the route ‘new’ to it. We added a Comment model and fixtures and linked them to the Post model via hasMany and belongsTo. We added the comments to the post.index template and added a route with a path for each post.comment so we could link to them. We created the comments.new template with a form that we could submit and the CommentsNewController with a save action to create the new comment.

Ember JS: MVC in the browser. Part 2 – Templates

In part one we added some routes defining our posts resource and for an individual post. We also learned how to inspect our Router object to see what we had defined using App.Router.router.recognizer.names in the web console. Now to create some templates to display when we transition through our routes. Download the code here. In part one we defined an application template with a handlebars {{outlet}}. We will add templates to render in this outlet.

Go back to index.html and add the following:

<script type="text/x-handlebars" data-template-name="index">
  {{#linkTo 'posts'}}Posts{{/linkTo}}
</script>

<script type="text/x-handlebars" data-template-name="posts">
  Posts
</script>

Refresh the page and you should see a link to ‘Posts’ under the title, clicking on it will take you to a page with the title and the word ‘Posts’. Looking at the console you can see that we transitioned from index to posts. Both of these templates were rendered in the main application template in the {{outlet}} block. Next we can add a link to individual Posts inside the Posts template and create a template for these individual Posts.

Change the ‘posts’ template to the following

<script type="text/x-handlebars" data-template-name="posts">
  <ul>
    {{#each model}}
      <li>{{#linkTo post this}}{{title}}{{/linkTo}}</li>
    {{/each}}
  </ul>
</script>

However, if you refresh the ‘/posts’ page you will no longer see anything. That is because we have no models and we definitely have no data. To remedy this we will add some. We start by creating a store. Go to app.js and add the following

App.Store = DS.Store.extend({
  revision: 12,
  adapter: 'DS.FixtureAdapter'
});

If you refresh the page you will probably see an error along the lines of ‘Undefined variable: DS’. This is because we need to add the ember-data library. To get it you can either clone the ember-data repository and run rake dist (https://github.com/emberjs/data#getting-ember-data) or download the latest build from http://builds.emberjs.com). In the future ember-data may be bundled along with ember but until then this is the way to get it.

So, download ember-data, add a <script> tag to index.html to load it, placing it after ember but before app.js, and refresh again. Now, the revision: 12 part is to check that it is compatible with the emberjs you are using, if not it will be reported in the console. The adapter part is optional but we are telling it to load data from fixtures that we will specify in javascript. Reload and all should be ok. We will now define a post model and some fixtures. Add the following to app.js

App.Post=DS.Model.extend({
  title: DS.attr('string')
});

App.Post.FIXTURES=[{ id: "1", title: "First Post" }, { id: "2", title: "Second Post" }, { id: "3", title: "Third Post" }];

Refresh the page and…..nothing. We have to tell ember what to do when it gets to the posts route. Add the following to app.js, after the main routes definition.

App.PostsRoute=Ember.Route.extend({
  model: function(){
    return App.Post.find();
  }
});

Here we tell ember that when we transition to ‘posts’ we need to find all the Post models that exist, in this case we return all the fixtures we have created. Refresh and all the posts should be shown using the title as a link. If you look at the definition for the posts template we created earlier you can see that we iterate over each of the models that the PostsRoute returns and add a link for each with the title as the text. {{title}} is a handlebars helper which tells it to get the attribute title from the current model in scope. Refresh the page and you should see links to 3 posts, hover over them to see the urls they link to. They should be linking to /posts/1 etc. Click on one of the links and you will see that we transition into post.index but there is nothing shown. Surprise, surprise we need a to define a template. Add the following to index.html after the currently defined templates:

<script type="text/x-handlebars" data-template-name="post/index">
  {{title}}
</script>

Refresh the page and you will see this warning in the console:

WARNING: the immediate parent route ('a') did not render into the main outlet and the default 'into' option ('p') may not be expected

Rember earlier we defined the {{outlet}} in the application template, ember expects every parent template to have an {{outlet}} into which it can render its children. In this case it is saying that it wants to render post.index into post but it can’t find it. We need to define a post template with the following in index.html

<script type="text/x-handlebars" data-template-name="post">
  Post
  {{outlet}}
</script>

Don’t forget to add the ‘Post’ text in the template, you will see why in a second. Refresh and you will see the ‘Post’ text but no title for the post. Well, it turns out there is a ‘feature’ (polite term for a bug) which means that in the current version the ‘default’ behaviour for a parameterised route does not work out of the box. Ember should know that because we defined the route with /posts/:post_id that it should return the model with id equal to :post_id when we transition to post.index with posts/1 etc but for the moment we have to tell it. We need to define a PostIndexController to get things back on track. Add the following code to app.js so we can see what is happening:

App.PostIndexRoute=Ember.Route.extend({
  model: function(params) {
    return App.Post.find(params.post_id);
  }
});

Refresh and….still nothing. Ok, I knew that was going to happen, unfortunately the bug means that empty params are passed to a nested route. Use your debugger and examine the params object yourself. All is not lost however, change the model hook to

model:function(params) {
  return this.modelFor('post');
}

Refresh. Yes, you should now see the text ‘Post First Post’. You can remove the unneeded ‘Post’ text from the post template and refresh again. Try going to posts/2 and posts/3 to convince yourself that it is all working.

So to recap. We have created an ember application which logs transitions between routes. It has 3 routes: index, posts and post.index. We have 5 templates: the main application one with {{outlet}}. The index template ie ‘/’. The posts template. A post template with an outlet for its nested routes and a post.index template to ‘show’ each post. In part 3 we will look at nested routes.

Ember JS: MVC in the browser. Part 1 – Routing

Ember JS is a Javascript client side MVC framework featuring all your favourite constructs such as controllers, models, views and routing. Client side means that it all runs inside the browser meaning that rendering times are extremely fast with no calls to the server to fetch pages. Ember has an optional restful data model called ember-data which handles the GET/PUT/POST/DELETE lifecycle of your models. Go to http://emberjs.com and download the starter kit which I used as the skeleton for this application or download the complete code for this part of the tutorial here. Have a look at the video tutorial as well, you will no doubt recognise some overlap with these posts. I primarily wrote this to get everything I had learned about ember in one place, I found the emberjs.com tutorials very useful but also unordered and was constantly flicking between various different sites, stack overflow etc. We will be building a simple blog application using a lot of the facts that can be found in the emberjs.com tutorials but with some pointers and helpers along the way. We will look at the ember components needed to handle the model lifecycle and views and later will write server side code to find, create and save the models. You can use any server framework you like but I prefer to use rails however ASP.net or whatever are just as good as long as they can handle REST and JSON. We will also look at using CORS or JSONP to load data directly to the browser.

For me it was easier to think of an ember app in terms of a rails one so where better to start than with the routes. Our simple application will consist of posts which can have many comments. In Rails (3.x) your routes file would consist of a root and some resources corresponding to the models. So lets say you use some scaffolding to get started

rails new blog
rails generate scaffold Post name:string title:string content:text

This gives us the following in config/routes.rb

resources :posts

We would also normally add a root after deleting public/index.html

root :to => 'welcome#index'

rake routes shows:

posts GET /posts(.:format) posts#index
POST /posts(.:format) posts#create
new_post GET /posts/new(.:format) posts#new
edit_post GET /posts/:id/edit(.:format) posts#edit
post GET /posts/:id(.:format) posts#show
PUT /posts/:id(.:format) posts#update
DELETE /posts/:id(.:format) posts#destroy
root / welcome#index

We want to create a similar route structure in ember.

To begin with we will keep all the html and UI bits in index.html and all the application logic in app.js (look inside the js directory to find it). The ember starter pack has an example index.html, open it in your browser and you will see that it renders a simple page with 3 colours listed. Have a look at it in an editor and you can see the script blocks within the html that define the templates. There is one with an {{outlet}} and an index template. If you look at js/app.js you will see that the IndexRoute returns an array of strings which are rendered into the index template. The templates use handlebars which is similar to erb in rails but is contained within javscript <script> blocks with a type of “text/x-handlebars”. Unfortunately we don’t have the benefit of rails logging what is happening when we go to a url in our application but we can ask ember to tell us what route it is rendering, to do this we will change the main ember application javascript. Open up js/app.js in an editor and change App=Ember.Application.create() to

App=Ember.Application.create({
  LOG_TRANSITIONS: true
});

Log transitions will help us to see what the router is doing when we move from page to page. If you open up a browser console and reload the index page you will see that it says “Transitioned into ‘index'”

We will now add our own bits to the starter app. Start by removing the existing handlebars templates from index.html and add the one shown below. Remember to place it within the html <body> tag.

 

<script type="text/x-handlebars">
  <body>
    <header>
      <h1>Blog</h1>
    </header>
    <div>
      {{outlet}}
    </div>
    <footer>
      &copy;2013 Me.
    </footer>
  </body>
</script>

 

Within this script block we have defined a template which has type text/x-handlebars, this is the basic application template into which other templates will be rendered, as you saw with the index route earlier. Anything wrapped in {{ }} tells handlebars to do something. The {{outlet}} is similar to <%= yield %> in rails, the placeholder where other templates are rendered.
We also need to create our application inside app.js. Remove the section with App.IndexRoute, leaving only the App definition with the LOG_TRANSITIONS we added

App=Ember.Application.create({
  LOG_TRANSITIONS: true
)};

We will start by defining a simple router, add the following to app.js

App.Router.map(function() {
});

and then populate it with a route

App.Router.map(function() {
  this.resource('posts');
});

Refresh the page and you will see that we have transitioned into index but there is nothing there except the header and footer text we specified. That is expected since we removed the index template, we will add them back during part 2. We can use App.Router.router.recognizer.names in the web console to see all the routes. It returns a js object within which all the current routes are show. Use it and you will see that we have the routes index and posts. Seems that it does not behave like you think it would in rails and only creates 2 named routes ‘index’ and ‘posts’ ie ‘/’ and ‘/posts’. What we also need is /posts/:id. To do this we have to create a route for instances of a post.

App.Router.map(function() {
  this.resource('posts');
  this.resource('post', { path: '/posts/:post_id' } );
});

Again this doesn’t quite get us what we want and only gives us the ‘index’, ‘posts’ and ‘post’ routes which will work fine but we want member routes under ‘post’ so that we can show, edit, delete etc. To begin with what we actually want is ‘index’, ‘posts’ and ‘post.index’. ‘post.index’ is ‘/posts/:post_id’ which in ember terms is the equivalent of rails route ‘show’. We want a page which shows all posts and one which shows a single post. We need to add some more to the router for this.

App.Router.map(function() {
  this.resource('posts');
  this.resource('post', { path: '/posts/:post_id' }, function() {
  });
});

We have defined an inner function of the ‘post’ resource where the member routes will be defined. You get ‘post.index’ for free. To look at all the routes you have defined again use App.Router.router.recognizer.names in a browser console. We now have ‘index’, ‘posts’ and ‘post.index’. We have some basic routes defined, now we need some UI magic. In part 2 we will look at templates and handlebars along with the models, store and routes to hook them together.