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 2

We left off in part 1 with the ability to list some kittens in the application. Now we are going to add the ability to create new kittens.

Client

First we are going to need a new page to create our kitten from. Let’s add a create route and some navigation to the existing app.

public/js/app.js
1
2
3
App.Router.map(function() {
  this.route('create');
});

This maps a new url create to our app. When the user visits that url Ember.js will automatically render the create template in the application template’s outlet.

public/index.html
1
2
3
4
5
6
7
8
9
<script type="text/x-handlebars">
<div class="container">
  <ul class="nav nav-tabs">
    <li>{{#linkTo index}}Index{{/linkTo}}</li>
    <li>{{#linkTo create}}Create{{/linkTo}}</li>
  </ul>
  {{outlet}}
</div>
</script>

Here we add two links to the top of the page. Ember will wire up proper page links for us when using the linkTo helper.

public/index.html
1
2
3
4
5
6
<script type="text/x-handlebars" data-template-name="create">
<form {{action save on="submit"}} class="form-inline">
  {{input type="text" value=name}}
  <button type="submit" class="btn btn-primary">Save</button>
</form>
</script>

For our create view let’s use a simple form that when submitted it will trigger the save action in a controller.

Using the input helper we create a text field and bind it to the controller’s name property.

public/js/app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
App.CreateController = Ember.Controller.extend({
  name: null,
  save: function() {
    var kitten = App.Kitten.createRecord({
      name: this.get('name')
    });

    kitten.save().then(function() {
      this.transitionToRoute('index');
      this.set('name', '');
    }.bind(this));
  }
});

The CreateController contains the name property and a save function. The name property is bound to the text box in the create view.

The save function will be called once the user submits the form.

To create a new kitten record on the client side we use createRecord.

Ember Data’s save function returns a promise. This promise is resolved when the server responses successfully. When that happens we want to transition to our list of kittens and empty out clear out the fields input.

Server Side

server.go
1
2
3
4
5
6
7
8
9
type Kitten struct {
    Id      int    `json:"id"`
    Name    string `json:"name"`
    Picture string `json:"picture"`
}

type KittenJSON struct {
    Kitten Kitten `json:"kitten"`
}

We need to declare two new types for our server side kittens. The first Kitten is a simple struct that has our three fields: id, name, and picture.

The second structure serves as our external data structure. Ember Data expects single Kittens to be wrapped in a named json object. For example:

single kitten response
1
{ "kitten": { "id": 1, "name": "Ben" } }

Ember will also send Kittens to the server wrapped in this style request.

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
func CreateKittenHandler(w http.ResponseWriter, r *http.Request) {
    // Parse the incoming kitten from the request body
    var kittenJSON KittenJSON
    err := json.NewDecoder(r.Body).Decode(&kittenJSON)
    if err != nil {
        panic(err)
    }

    // Grab the kitten and set some dummy data
    kitten := kittenJSON.Kitten
    kitten.Id = 5
    kitten.Picture = "http://placekitten.com/300/200"

    // Serialize the modified kitten to JSON
    j, err := json.Marshal(KittenJSON{Kitten: kitten})
    if err != nil {
        panic(err)
    }

    // Write the response
    w.Header().Set("Content-Type", "application/json")
    w.Write(j)
}

To create a kitten we need to define a function that will handle the route.

In order to gain access to the incoming json request from Ember Data we need to decode the request’s body. To do this we use a json.Decoder. We new up a decoder and decode the payload into an instance of KittenJSON. The decoder will populate all the data for us according to the struct’s definition.

Because we want to get this example up and running as quickly as possible I am just manually setting an Id and Picture. The Name property will be set from the incoming json.

We then need to return a response to the client so we serialize our kitten back into a KittenJSON type and write it to the response writer.

server.go
1
2
3
4
func main() {
  // ... omited rest of function
  r.HandleFunc("/api/kittens", CreateKittenHandler).Methods("POST")
}

We need to tell the server when to create a kitten. The create behavior for Ember Data is a POST to the index end point. The Gorilla Mux makes this easy by allowing us to specify the method.

All the wiring complete

All the wiring for this evolution of the app is now complete. We can click the create link, fill out the form, and show the user that we created a kitten.

In Memory Storage

Now the we can create kittens let’s rig up an in memory store to make the application feel slightly more complete.

server.go
1
2
3
4
5
6
7
8
9
10
11
12
13
var kittens []Kitten

func CreateKittenHandler(w http.ResponseWriter, r *http.Request) {
    // ...

    kitten := kittenJSON.Kitten
    kitten.Id = len(kittens) + 1
    kitten.Picture = "http://placekitten.com/300/200"

    kittens = append(kittens, kitten)

    // ...
}

We first declare a new slice of kittens. This variable will exist as long as the server is running. Not production ready but it moves our app forward.

To add a new kitten we just append it to the current list of kittens. We also calculate the id of the kitten to be the length of the slice plus one.

server.go
1
2
3
4
5
6
7
8
9
10
11
12
type KittensJSON struct {
    Kittens []Kitten `json:"kittens"`
}

func KittensHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    j, err := json.Marshal(KittensJSON{Kittens: kittens})
    if err != nil {
        panic(err)
    }
    w.Write(j)
}

We can now make our index function dynamic.

Once again Ember Data expects a specific response structure. In this case it is the plural name of the model and an array. For example:

1
{ "kittens": [{"id": 1 ... }] }

Concludes part 2

We created some very simple in memory persistence on the server side. On the client side we demonstrated the typical create redirect work flow.

Next time we will handle delete and update.

Comments