Ember.js & Laravel
Flamur Mavraj (@oxodesign)

Ember.js & Laravel

Oslo Ember.js Meetup - 17.10.2013





Flamur Mavraj / @oxodesign

Laravel framework

Installing Laravel

> CMD
php composer.phar create-project laravel/laravel path

Laravel framework

Create a controller

> CMD
php artisan controller:make TodosController

Laravel framework

Add controller resource to the routes

app/routes.php
Route::resource('todos', 'TodosController');

Laravel framework

Create a new layout

app/routes.php

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="">
    <meta name="author" content="Flamur Mavraj">
    <link rel="shortcut icon" href="../../assets/ico/favicon.png">

    <title>Laravel + Ember.js</title>

    <link href="assets/css/bootstrap.css" rel="stylesheet">
    <link href="assets/css/app.css" rel="stylesheet">
</head>
<body>


<script src="assets/javascript/vendor/bootstrap.min.js"></script>
<script src="assets/javascript/app.js"></script>

</body>
</html>
                        

Laravel framework

Show the layout/view

app/routes.php

Route::get('/', function(){
    return View::make('index');
});
                    

Laravel framework

Configure your database

app/config/database.php

                            <?php
return array(
    // ...

    'default' => 'sqlite',

    // ...
);
                        

Laravel framework

Create a migration

> CMD
php artisan migrate:make todos

Laravel framework

Edit your migration-file

app/database/migrations/*_todos.php

// ...
public function up(){
    Schema::create('todos', function($table){
        $table->increments('id');
        $table->string('name');
        $table->boolean('isCompleted');
    });
}

// ...
public function down(){
    Schema::drop('todos');
}

                        

Laravel framework

Migrate to create the database table

> CMD
php artisan migrate

Laravel framework

Create a model for your Todo's

app/models/Todo.php

<?php
    class Todo extends Eloquent{
        public $timestamps = false;

        protected $fillable = array('name', 'isCompleted');
    }
                        

Laravel framework

Include your Ember.js scripts and other libraries, as well create your application template

app/view/index.php

<!DOCTYPE html>
<html lang="en">
<head>
    // ...
</head>
<body>

<script type="text/x-handlebars" data-template-name="application">
    <div class="container">
        {{outlet}}
    </div>
</script>

<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/handlebars.js/1.0.0/handlebars.js"></script>
<script src="http://builds.Ember.js.com/release/ember.js"></script>
<script src="http://builds.Ember.js.com/beta/ember-data.js"></script>
// ...
</body>
</html>

                        

Ember.js

Define your app

public/assets/javascript/app.js
var App = Ember.Application.create();

Ember.js

Map your routes

public/assets/javascript/app.js

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

Ember.js

Create a new template for the route

app/view/index.php

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

</script>
                        

Ember.js

Create an input view

public/assets/javascript/app.js

// ...
App.TodoTextFieldView = Ember.TextField.extend({
    classNames: ['form-control']
});
                        


Then, create e form on your "todos" template with an action listener on submit.

app/view/index.php

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

    <form {{action "add" on="submit"}}>
        {{view App.TodoTextFieldView valueBinding="todoName"}}<br />
    </form>
</script>
                        

Ember.js

Add your adapter to the store

public/assets/javascript/app.js

// ...
App.Adapter = DS.RESTAdapter.extend({
    namespace: "path/to/the/app"
});

App.Store = DS.Store.extend({
    adapter: App.Adapter
});


                        

Ember.js

Create your EmberData model

public/assets/javascript/app.js

// ...
App.Todo = DS.Model.extend({
    name: DS.attr('string'),
    isCompleted: DS.attr('boolean')
});
                        

Ember.js

Create TodosRoute, attach your model and add the action listner as well

public/assets/javascript/app.js

// ...
App.TodosRoute = Ember.Route.extend({

    model : function(){
        return this.store.find('todo');
    },

    actions : {
        add : function(){
            var self = this;
            var todo = this.store.createRecord('todo');

            todo.set('name', this.get('controller').get('todoName'));
            todo.set('isCompleted', false);

            todo.save().then(function(){
                self.get('controller').set('todoName', '');
                console.log('Todo added ...');
            });
        }
    }

});
                        

Laravel framework

Update your index and store-functions on your controller

app/controllers/TodoController.php

// ...

public function index(){
    return json_encode(array('todos' => Todo::all()->toArray()));
}

public function store(){
    $data = Input::json()->all();

    $todo = Todo::create($data['todo']);
    $todo->save();

    return json_encode(array('todo' => $todo->toArray()));
}
                        

Laravel framework

Update your update-function so it updates your todo on request

app/controllers/TodoController.php

// ...

public function update($id){
    $data = Input::json()->all();

    $todo   = Todo::find($id);
    $update = $todo->update($data['todo']);

    return json_encode(array('todo' => $todo->toArray()));
}
                    

Laravel framework

And finally our destroy(delete)-function

app/controllers/TodoController.php

// ...

public function destroy($id){
    $todo = Todo::find($id);
    $delete = $todo->delete();

    if($delete){
        return 'true';
    }else{
        return 'false';
    }
}
                    

Ember.js

Create an empty TodoController

public/assets/javascript/app.js

// ...
App.TodoController = Ember.ObjectController.extend({

});
                        

Ember.js

Loop through your todo's from the TodoController, bind the attribute isCompleted on the checkbox and add the action on the button

public/assets/javascript/app.js

// ...

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

    <ul class="list-unstyled">
        {{#each itemController="todo"}}
        <li {{bind-attr class="isCompleted:text-muted isCompleted:done"}}>
                        <button {{action delete this}} class="btn btn-danger btn-xs">x</button>
                        {{view Ember.Checkbox checkedBinding="isCompleted"}} {{name}}</li>
        {{/each}}
    </ul>

</script>
                        

Ember.js

Create an observer to track changes and save the model

public/assets/javascript/app.js

// ...

App.Todo = DS.Model.extend({

    // ...
    changeObserver: function(){
        if(this.get('isDirty') == true && !this.get('isNew')){
            this.save().then(function(){
                console.log('Model changed and saved!');
            });
        }
    }.observes('isCompleted', 'name')
});

                        

Ember.js

Create delete functionality on your TodoController

public/assets/javascript/app.js

// ...
App.TodoController = Ember.ObjectController.extend({
    actions : {
        delete : function(model){
            if (confirm("Are you sure you want to delete the selected record ? Click OK to continue.")) {

                var self = this;

                //deletes record from store
                model.deleteRecord();

                //persist change
                model.save().then(function(){
                console.log('Todo deleted');
                });
            }
        }
    }
});
                        

Ember.js

Add "clear all completed" button on your template

app/view/index.php

// ...

<button {{action clearAllCompleted}} class="btn btn-danger btn-xs">Clear all</button>
                        


And the functionality for that

public/assets/javascript/app.js

// ...
App.TodosController = Ember.ArrayController.extend({

    actions : {
        clearAllCompleted : function(){
            var completed = this.get('content').filterProperty('isCompleted', true);
            completed.invoke('deleteRecord');
            completed.invoke('save');
        }
    }
});
                        

Demo

See the app in action

Flamur Mavraj

@oxodesign