HEY! I'm working on an awesome course on Golang and Ember.js skills that's live soon. Get early access to it here:

nerdyworm

Building an App With Ember.js and Go - Part 1

Today we embark on a magical jounry of Gophers and Tomsters. In open source the projects with the cutest mascots always gain traction!

With the power of the internets cutest mascots combine we will create the most basic crud app to help you hit the ground running with Ember.js and Go.

Up and Running

The first part of any Ember project is to get the basic up and running. Since this is a super basic example we are just going to wire up a Go server to serve our static assets.

directoy layout
1
2
3
├── public
│   └── index.html
└── server.go
server.go
1
2
3
4
5
6
7
8
9
10
package main

import (
  "net/http"
)

func main() {
  http.Handle("/", http.FileServer(http.Dir("./public/")))
  http.ListenAndServe(":8080", nil)
}

We now have two files. An index.html file with hello world in it and a sever.go file that will serve statis assets out of the public directory.

To run this all you need to do is

1
go run server.go

You should be able to visit http://localhost:8080 and see hello world in your browser.

Personally I feel uneasy that we are not giving ourselves some feedback of what the program is doing so let’s add some logging.

server.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import (
  "net/http"
  "log"
)

func main() {
  log.Println("Starting Server")
  http.Handle("/", http.FileServer(http.Dir("./public/")))

  log.Println("Listening on 8080")
  http.ListenAndServe(":8080", nil)
}

That makes me feel much better.

Setup Ember

Now that we have a basic http server we can setup our Ember environment.

I have included a script that will grab jQuery, Handlebars, and Ember for your. These are all the dependencies that you need to get the project up and running.

Relevant XKCD: Dependencies

script/js_deps.sh
1
2
3
4
5
6
7
8
9
10
#!/bin/bash

set -e

mkdir -p public/js/lib

wget -O public/js/lib/jquery.js http://code.jquery.com/jquery-2.0.0.js
wget -O public/js/lib/handlebars.js https://raw.github.com/wycats/handlebars.js/1.0.0-rc.4/dist/handlebars.js
wget -O public/js/lib/ember.js http://builds.emberjs.com.s3.amazonaws.com/ember-latest.js
wget -O public/js/lib/ember-data.js http://builds.emberjs.com.s3.amazonaws.com/ember-data-latest.js

Now that we have all our dependencies let’s wire them up in the index.html file.

public/index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Ember.js + Go == ♥</title>
  <link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css" rel="stylesheet">
</head>
<body>
<script src="js/lib/jquery.js"></script>
<script src="js/lib/handlebars.js"></script>
<script src="js/lib/ember.js"></script>
<script src="js/lib/ember-data.js"></script>
</body>
</html>

All we needed to do is add a few script tags and a style sheet. The order of the tags is important since ember depends on handlebars and jQuery.

app.js
1
2
3
4
5
(function() {

  var App = Ember.Application.create();

})();

Now we can actually create our ember application. The canonical example name space is App, but you can use any name that you would like.

This line of code bootstraps your Ember application. It will automatically run once the page is loaded.

Don’t forget to include the new file in our index.html file after all the lib scripts.

1
<script src="js/app.js"></script>

When you view your application in the browser you should see the following output in your developer console. If you do not see this then something is broken. Make sure that all your files are loading correctly and that they are in the correct order.

1
2
3
4
5
DEBUG: -------------------------------
DEBUG: Ember.VERSION : 1.0.0-rc.3
DEBUG: Handlebars.VERSION : 1.0.0-rc.4
DEBUG: jQuery.VERSION : 2.0.0
DEBUG: -------------------------------

And last but not least we need to define our application template. This is easily accomplished by adding the following script tag to the index.html.

There are various ways of dealing with handlebars templates, but to avoid the complexities of build tools we are just going to use this method.

index.html
1
2
3
<script type="text/x-handlebars">
<h1>Hello Ember.js</h1>
</script>

When you open up the page in the browser you should see Hello Ember.js

RESTFull kittens

Let’s create our kittens endpoint.

First we are going use the super excellent Gorilla web toolkit’s mux for routing.

Install the dependency using the go get command.

1
go get github.com/gorilla/mux

The Gorilla mux provides a ton of wonderful functionality that we will take advantage of later. For now a simple GET endpoint is all we need to move this application forward.

server.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main

import (
  "net/http"
  "log"
  "github.com/gorilla/mux"
)

func KittensHandler(w http.ResponseWriter, r *http.Request) {
  w.Header().Set("Content-Type", "application/json")
  w.Write([]byte(`{"kittens": [
    {"id": 1, "name": "Bobby", "picture": "http://placekitten.com/200/200"},
    {"id": 2, "name": "Wally", "picture": "http://placekitten.com/200/200"}
  ]}`))
}

func main() {
  log.Println("Starting Server")

  r := mux.NewRouter()
  r.HandleFunc("/api/kittens", KittensHandler).Methods("GET")
  http.Handle("/api/", r)

  http.Handle("/", http.FileServer(http.Dir("./public/")))

  log.Println("Listening on 8080")
  http.ListenAndServe(":8080", nil)
}

I prefer to have my api endpoint under an /api name space. This is not a requirement, but a personal preference.

Ember Data expects a very particular response. You want to wrap a collection response with the plural of the model name. So in our case the root should be kittens and the value of kittens should be an array of kitten objects.

The spec for this is currently being fleshed out here: http://jsonapi.org/

Display the Kittens

Now that we have some data from the server we can display it in our Ember app.

public/js/app.js
1
2
3
4
5
6
DS.Store.create({
  revision: 12,
  adapter: DS.RESTAdapter.create({
    namespace: 'api'
  })
});

The first thing we need to do is create a data store. We also need to tell the store’s adapter that we want to namespace ajax calls under the api namespace.

public/js/app.js
1
2
3
4
App.Kitten = DS.Model.extend({
  name: DS.attr('string'),
  picture: DS.attr('string')
});

The next thing we can do is define our Kitten model. We extend a DS.Model and define the attributes name and picture.

public/js/app.js
1
2
3
4
5
App.IndexRoute = Ember.Route.extend({
  model: function() {
    return App.Kitten.find();
  }
});

Ember will automatically route to the index route of an application. We can alter the behavior of the index by defining an App.IndexRoute. This is where we can hook into the model function and return our kittens.

public/index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script type="text/x-handlebars">
<div class="container">
  {{outlet}}
</div>
</script>

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

<ul class="thumbnails">
{{#each controller}}

  <li class="span3">
    <div class="thumbnail">
      <img {{bindAttr src="picture"}}/>
      <div class="caption">
        <h3>{{name}}</h3>
      </div>
    </div>
  </li>

{{/each}}
</ul>

</script>

We modified the application template and added a new index template.

The application template now has an outlet. This is where ember will insert the index template or any other template defined by the current route.

In the index template we use the each helper to loop over all the kittens and output them with some basic markup.

In order to display the image we need to use the bindAttr helper. This will ensure that the src attribute is bound to the models picture property.

End of part 1

It took a bit of work to get this example up and running, but things assuming you were able to follow along you should have a fully integrated ember.js app with a go back end.

In the next part we will focus on adding new pages and some editing.

Happy Embering and Gophering :)

Full Source

The full source code to this example can be found on my github account.

Comments