Ember App Kit

Na Ember.js een jaar geleden uitgeprobeerd te hebben tijdens een hackathon op school ben ik me er opnieuw in gaan verdiepen. Dit keer ben ik gaan werken met de Ember App Kit. Als oefen project ben ik mijn Films op TV pagina om gaan zetten naar Ember. De belangrijkste bevindingen zal ik behandelen.

De applicatie heeft van twee locaties gegevens nodig, FilmTotaal en Rotten Tomatoes. De gegevens van FilmTotaal worden gecached op mijn eigen site en de API van Rotten Tomatoes (RT) wordt direct aangesproken. In de model van Movie is een belongsTo koppeling gemaakt met de Tomato model. De Tomato model kan dan later door een eigen adapter de RT API aanspreken.

/filmsoptv/blob/master/app/models/movie.js
1
2
3
4
5
6
7
8
9
var Movie = DS.Model.extend({
    titel: DS.attr('string'),
    // regels verwijderd
    synopsis: DS.attr('string'),
    // koppeling met tomato model, die via de Rotten Tomatoes API de beoordelingen ophaald
    tomato: DS.belongsTo('tomato', {async: true})});
 
export default Movie;

De attributen hoeven niet in de DS.Model gedefinieerd te worden, het type wordt dan automatisch herkend. Zijn het echter array’s dan zullen ze wel gedefinieerd moeten worden.

/filmsoptv/blob/master/app/models/tomato.js
1
2
3
4
5
6
7
var Tomato = DS.Model.extend({
    ratings: DS.attr(),
    links: DS.attr(),
    // regels verwijderd
});
 
export default Tomato;

Ember gebruikt standaard het id attribuut als primary key. Voor de koppeling met FilmTotaal is echter het imdb id nodig. Deze is bij RT beschikbaar in de alternate_ids array. Op de volgende wijze kan het imdb id als primary key gedefinieerd worden.

export default DS.JSONSerializer.extend({
    primaryKey: 'alternate_ids.imdb'
});

Op de index moeten twee lijsten ingeladen worden. Met Ember.RSVP.hash kunnen de twee lijsten opgehaald worden.

/filmsoptv/blob/master/app/routes/index.js
1
2
3
4
5
6
7
8
9
10
11
var indexRoute = Ember.Route.extend({
  model: function() {
//  return this.store.find('list', 1); // old
    return Ember.RSVP.hash({
        moviesToday: this.store.find('list', 1),
        moviesTomorrow: this.store.find('list', 2)
    });
  }
});
 
export default indexRoute;

De lijst is vervolgens op volgende manier uit te lezen.

{{#each movie in moviesToday.movies}}
   {{{movie.titel}}}
{{/each}}

Als laatst moet ook de Bootstrap accordion geïmplementeerd worden. Na wat zoek werk ben ik uit de gekomen op de implementatie uit deze Gist op Github.

Ember.js en Laravel uitproberen

Voor een project van school ben ik twee weken bezig geweest met Ember.js, Ember Data en Laravel. Met het Laravel PHP framework heb ik de API geschreven die gebruikt wordt door Ember.js en de iPhone applicatie.

Ember.js is een Javascript Framework dat werkt volgens het MVC principe. Het was voor mij de eerste keer dat ik hier mee werkte, hierdoor moet ik veel uitzoeken en proberen. Ember.js heeft op dit moment al bijna versie 1 bereikt en is daardoor al bijna een stabiele versie. Ember Data daarin tegen is nog volop in ontwikkeling. De communicatie tussen Ember en de API werkend krijgen koste veel tijd en regelmatig wat frustraties.

Het uiteindelijke resultaat is een pagina die een lijst van bewegingsopnames laat zien en een details pagina die grafiek laat zien met bewegingen.

De site is hier te bekijken: http://bci.remcoraaijmakers.nl/

Tijdens de ontwikkeling ben ik tegen verschillende problemen aangelopen. Hier volgen twee tips.

Lijst en details view

In veel gevallen wil je in een website gegevens laten zien. Bij deze applicatie zijn er verschillende opnames van bewegingen, al deze opnames worden in een lijst weergegeven. Als je op een item (exercise) klikt wil je hiervan meer informatie krijgen, namelijk alle logs (motionlogs).

Een belangrijk probleem waar ik tegen aan liep was het laden van de details gegevens van een exercise. Aan de hand van dit (code) voorbeeld kreeg ik een lijst + details weergave werkend. Echter werd bij het openen van de details niet de bijbehorende motionlog gegevens opgehaald via de API. Door de zelfde model voor de lijst en de details te gebruiken werd bij het laden van de details de gecachede data gebruikt van de lijst.

De oplossing heb ik uiteindelijk hier gevonden. Het advies was om een aparte model aan te maken voor de lijst.

1
2
3
4
5
6
7
8
/*
* List model
* Om een aparte lijst te maken
*/
App.ExerciseListing = DS.Model.extend({
name: DS.attr('string'),
createdAt: DS.attr('string')
});

Deze model heeft ook een eigen onderdeel nodig in de API. In mijn geval heet de model in Ember “ExerciseListing”. Daarvoor is volgende endpoint nodig in de api “.nl/api/v1/exercise_listings“.

Om de lijst met exercises te laden bij het openen van de moet de standaard route van Ember.js aangepast worden. De hierboven genoemde model moet namelijk ingeladen worden.

1
2
3
4
5
6
7
8
/*
* Default route
*/
App.ApplicationRoute = Ember.Route.extend({
model: function() {
return App.ExerciseListing.find();
}
});

Opbouw van Ember en data laden via Ember Data

Bij het laden van de details van een exercise wordt de telefoon data via de API geladen. Bij het openen van de details pagina wordt door Ember.js direct de html weergeven. Ember laad hierna pas de data in de html.

Om de grafiek weer te geven is moet eerst de motionlog data geladen zijn. Om de grafiek pas te laten genereren als de data aanwezig is heb ik een javascript timeout gebruikt.

1
2
3
4
5
6
7
8
9
10
11
12
13
App.ExerciseRoute = Ember.Route.extend({
model: function(params) {
return App.Exercise.find(params.exercise_id);
},
setupController: function(controller, model) {
var data = App.Exercise.find(model.id);
controller.set('model',data);
controller.set('content',data);
setTimeout(function(){
controller.addGraph();
}, 3000);
}
});