smart | Webentwicklung
Alles rund um HTML5, PHP, WordPress & Co.

Todo-App: 7. Daten mit Ruby on Rails per REST-API speichern

21. Januar 2013
Stephan
Todo-App mit backbone.js und CoffeeScript entwickeln

Eigentlich hatte ich das Todo-App-Tutorial bereits abgeschlossen, aber nun habe ich mich dazu entschlossen noch einen Teil hinzufügen.

So soll es in diesem 7. Teil darum gehen, die Daten (Aufgaben bzw. Todos) nicht mehr lokal im Browser, sondern serverseitig zu speichern.

Auf Serverseite werden wir hierfür das Webframework Ruby on Rails einsetzen. Die Kommuniaktion zwischen clientseitigen backbone.js und serverseitigen Ruby on Rails wollen wir dabei über eine REST-API realisieren.

Teile der Artikel-Serie

Zur Übersicht eine Auflistung aller erschienenen Artikel der Artikel-Serie „Tutorial: Todo-App mit backbone.js & CoffeeScript erstellen“:

  1. Vorbereitungen vornehmen (Projektverzeichnis anlegen etc.)
  2. Model erstellen
  3. Collection – Liste von Models
  4. View-Struktur & Grundlagen einer backbone.js View
  5. Restliche Views implementieren
  6. Controller implementieren & App fertigstellen
  7. Daten mit Ruby on Rails per REST-API serverseitig speichern

Ruby on Rails Projekt(-verzeichnis) erstellen & Anpassungen

Vorab sei gesagt, dass ich hier nicht komplett ins Detail gehen möchte. Ich setze also voraus, dass ihr euch grundlegend mit Ruby on Rails auskennt. Ansonsten schaut euch die Grundlagen einfach im Rails-Guide an: Ruby on Rails Guide.

Jetzt müssen wir die folgenden Schritte abarbeiten:

  • neues Ruby on Rails Projekt erzeugen
  • kopiert unser erstelltes Todo-Projektverzeichnis (siehe Teil 1) in euer Rails-Projektverzeichnis und zwar ins Verzeichnis app/assets/javascripts
  • nun kopiert die styles.css
    von app/assets/javascripts/TodoApp/public/css
    nach app/assets/stylesheets
  • analog verfahrt ihr mit den Bilder: kopiert alle Bilder
    von app/assets/javascripts/TodoApp/public/images
    nach app/assets/images
  • öffnet nun die eben kopierte styles.css und passt die Pfade zu den Bildern an
  • offnet die application.js in app/assets/javascripts und nehmt folgende Anpassung vor:
    //= require jquery
    //= require ./TodoApp/app/lib/underscore-1.4.2.min.js
    //= require ./TodoApp/app/lib/backbone-0.9.2.min.js
    //= require ./TodoApp/app/app.js
    //= require ./TodoApp/app/models/Todo.js
    //= require ./TodoApp/app/collections/TodoCollection.js
    //= require ./TodoApp/app/views/TodoStatsView.js
    //= require ./TodoApp/app/views/TodoListItemView.js
    //= require ./TodoApp/app/views/TodoListView.js
    //= require ./TodoApp/app/views/TodoView.js
    //= require ./TodoApp/app/controllers/TodoController.js
    //= require ./TodoApp/app/main.js
    

Todo-App (backbone.js-Code) anpassen

Als nächstes müssen wir unsere Todo-App, sprich den clientseitigen backbone.js-Code anpassen. Wir wollen ja nun nicht mehr die clientseitige localStorage-API, sondern eine serverseitige REST-API nutzen.

Als erstes passen wir unser Todo-Model an. Dazu öffnet die Todo.coffee und löscht folgende Zeile:

url: '/todo'

Anschließend öffnet die TodoCollection.coffee, da wir auch hier eine Anpassung vornehmen müssen. Dafür ersetzen wir die folgende Zeile:

localStorage: new Store('TodoApp')

mit

url: '/todos'

Abschließend müssen wir noch unseren TodoController anpassen. Also öffnen wir die TodoCollection.coffee und nehmen folgende Änderuneg vor:

class TodoApp.Controllers.TodoController extends Backbone.Router
    routes:
        '': 'index'

    initialize: ->
        Backbone.history.start()

    index: ->
        todos = new TodoApp.Collections.TodoCollection()
        todos.fetch(
            success: ->
                todoListView = new TodoApp.Views.TodoListView(todos: todos)
                todoStatsView = new TodoApp.Views.TodoStatsView(todos: todos)
                todoView = new TodoApp.Views.TodoView(
                    todos: todos,
                    todoListView: todoListView,
                    todoStatsView: todoStatsView
                )

                todoView.render()
        )

JavaScript:

TodoApp.Controllers.TodoController = Backbone.Router.extend({
    routes: {
        '': 'index'
    },

    initialize: function()
    {
        Backbone.history.start();
    },

    index: function()
    {
        var todos = new TodoApp.Collections.TodoCollection();
        todos.fetch({
            success: function()
            {
                var todoListView = new TodoApp.Views.TodoListView({todos: todos});
                var todoStatsView = new TodoApp.Views.TodoStatsView({todos: todos});
                var todoView = new TodoApp.Views.TodoView({
                    todos: todos,
                    todoListView: todoListView,
                    todoStatsView: todoStatsView
                });
                todoView.render();
            }
        });
    }
});

Wir haben hier nichts weiter gemacht als die Initialisierung und das Rendern der TodoView erst aufzurufen, wenn das fetch erfolgreich war. Das bedeutet, dass die Daten korrekt vom Server empfangen/geladen werden konnten und in die Collection eingefügt wurden. Das ist wichtig, weil die Kommunikation zwischen Client und Server eine gewissen Zeit in Anspruch nimmt. Hätten wir den Code nicht innerhalb des success-Callbacks, dann würde bzw. könnte die Initialisierung und das Rendern schon aufgerufen werden, bevor die Daten vom Server empfangen wurden.

Rails Model und dazugehörige Migration erstellen

Nun kommen wir zum eigentlichen Ruby on Rails Code. Als erstes benötigen wir analog zum clientseitigen Todo-Model ein serverseitiges Model anlegen. Dazu erstellt eine Datei namens todo.rb im Model-Verzeichnis (app/models) und fügt folgenden Code ein:

class Todo < ActiveRecord::Base
    attr_accessible :done, :title
end

Weiterhin müssen wir nun noch eine Migrationsdatei anlegen. Legt dazu im Verzeichnis db/migrate eine neue Datei mit dem Namen create_todos.rb. In diese Migrationsdatei fügt ihr Folgendes ein:

class CreateTodos < ActiveRecord::Migration
    def change
        create_table :todos do |t|
            t.boolean :done
            t.string :title

            t.timestamps
        end
    end
end

Als nächstes erstellt die Datenbank mittels dem Rake-Task db:create und ruft direkt danach db:migrate auf. Dadurch erzeugen wir uns eine Tabelle in der unsere Aufgaben/Todos gespeichert werden.

View erstellen

Für die View legen wir unter app/views/todos eine index.html.erb-Datei an. Hier fügen wir einfach das HTML für unsere Todo-App ein:

<div id="todoWrap">
    <h1>Meine Aufgaben</h1>
    <input id="createItemInput" type="text" placeholder="Neue Aufgabe erstellen" />
    <ul id="todoList"></ul>
    <div id="todoStats"></div>
</div>

Controller zum Anlegen, Bearbeiten und Löschen von Aufgaben

Jetzt kommen wir zum Controller. Als erstes legen wir hierfür die Datei todos_controller.rb im Verzeichnis app/controllers an. Das Grundgerüst unseres Controllers sieht dabei wie folgt aus:

class TodosController < ApplicationController
    def index
    end

    def create
    end

    def update
    end

    def destroy
    end
end

Wir benötigen, wie ihr sehen könnt, also 4 Methoden, die wir nun implementieren wollen.

index:
Die index-Methode dient zum einen dazu, unsere HTML-Seite an den Client auszuliefern. Zum anderen aber auch dazu im JSON-Format alle existierenden Aufgaben/Todos an den Client zu senden.

Hierfür implementieren wir die Methode wie folgt:

def index
    @todos = Todo.all

    respond_to do |format|
        format.html # index.html.erb
        format.json { render json: @todos }
    end
end

create:
Kommen wir nun zur Methode create. Wenn clientseitig eine neue Aufgabe angelegt wird, sendet backbone.js standardmäßig eine HTTP-POST Anfrage an den Server mit den Daten. Die serverseitige create-Methode soll nun diese Daten in der Datenbank speichern:

def create
    @todo = Todo.new(params[:todo])

    if @todo.save
        render json: @todo
    else
        render json: @todo.errors
    end
end

update:
Analog benötigen wir die Methode update zum Speichern von Änderungen:

 def update
    @todo = Todo.find(params[:id])

    if @todo.update_attributes(params[:todo])
        head :ok
    else
        render json: @todo.errors
    end
end

destroy:
Als letztes fehlt dann nur noch eine Methode zum Löschen von existierenden Aufgaben:

def destroy
    @todo = Todo.find(params[:id])
    @todo.destroy

    render json: @todo
end

Routing

Zum Überblick und Verständnis hier noch einmal die jeweiligen HTTP-Anfrage-Methoden, URLs und dazugehörige Aktionen:

backbone.js ActionURLHTTP-MethodeRails Action
fetch/todosGETindex
create/todosPOSTcreate
update/todos/idPUTupdate
delete/todos/idDELETEdestroy

Damit das nun auch so funktioniert wie in der Tabelle aufgezeigt, müssen wir noch die routes.rb im Verzeichnis app/config anpassen. Dazu fügt einfach die folgende Zeile Code hinzu:

resources :todos

Das ganze sieht in Firebug dann so aus:

REST-API: backbone.js & Ruby on Rails

Fazit

Das Zusammenspiel von backbone.js und Ruby on Rails ist dank einer einheitlichen REST-API sehr einfach umzusetzen.

Nun habt ihr also kennengelernt, wie ihr die Aufgaben/Todos lokal als auch serverseitig speichern könnt. Interessant wäre jetzt z.B. auch eine On-/Offline-Synchronisation. Beispielsweise könnten die Daten immer lokal im Browser gespeichert werden, wenn keine Internetverbindung besteht. Sobald der Browser dann wieder online ist, könnte es eine Synchronisation mit den serverseitigen Daten geben.

Habt ihr eventuell noch weitere Ideen. Wie könnte man die Todo-App noch weiter anpassen? Welche Funktionen wären noch wünschenswert?

Kommentare  
0 Kommentare vorhanden
0 Trackbacks/Pingbacks vorhanden
Du bist herzlich eingeladen auch ein Kommentar zu hinterlassen!
Kommentar schreiben

Vielen Dank für dein Kommentar!