Single Page App with Laravel and EmberJS

Aleksander Koko
Tweet
This entry is part 4 of 4 in the series REST App with Laravel and EmberJS

REST App with Laravel and EmberJS

In this part, we will see how Ember works, how to use Ember Data and how to build something simple with it. Router, Route, Model, Template and Store are some of the concepts of Ember. I’m not going to explain every one of those, so if you feel stuck, use the documentation. As usual, you can download the code for this part here.

Let’s code

Note that while developing with Ember, it’s a good idea to download the Ember Inspector. They released Ember with a Chrome Extension and now that extension is also on Firefox.

For this example, we are going to put every line of JS inside /public/static/app.js. In a real project, this is not a good idea. This simplifies our example but ask yourself – have you ever done some serious work with MVC architecture in just one big file? We saw how Laravel works: controllers are in one folder, each of them in one file, the configuration is in its own folder, the models too. I suggest you do the same thing with Ember when you dive into a proper project.

The first thing you ever do when starting Ember is create the application. It is a global namespace for everything that you code with Ember. An Application can be created like this:

    App = Ember.Application.create();

I suggest activating a bit of debugging just by adding a line of code when you create the application.

    App = Ember.Application.create({
        LOG_TRANSITIONS: true
    });

It doesn’t do much more than output your movement through the URLs and templates in the console. Also, we are going to use Ember Data which is a separate module of Ember and provides a nice integration with REST, translating everything from Store Object to request on the server. By default, Ember Data uses the Rest Adapter. You can also use the Fixture Adapter for testing and local development. Basically, Ember Data is a bridge between servers (Rest API) and local storage with the Store Object.

As we saw earlier, our API uses a namespace. Ember’s Data comes with a Rest Adapter which accepts a namespace, a prefix like we saw on Laravel Route groups. Lets pass in our namespace as an argument.

    App.ApplicationAdapter = DS.RESTAdapter.extend({
        namespace: 'api/v1'
    });

The adapter now requests all the data via example.com/api/v1/.

Link the App Store with the Adapter and you are ready to start developing.

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

One of the main concepts of Ember is URL. Everything is built around that idea. The Router keeps the URLs and the templates synchronized. Inside the Router, you can define a resource and map that resource to a specific URL. In this example, we will work only with the photo resource and the user resource. Feel free to add the category resource and make some one to many relations with Ember. Don’t forget that earlier we created some relations (one-to-many and belongs-to) with Laravel, but we didn’t use them. Using one-to-many relations in Laravel is easy enough, but I don’t want to overwhelm you. If enough interest in generated in the comments, we’ll add this to our app in a followup post, along with pagination.

The Router is the place where all the routes should be defined. Here, we defined two resources with their URLs. The URL is optional here. :photo_id is an argument. Let’s say that we navigate to example.com/photo/2. What would happen? We have a resource that passes our request to the model or controller, and there we grab some data from the Store. If the Store doesn’t find it, it looks on the server. :photo_id can be used to retrieve this data. In this case it looks for example.com/api/v1/photos/2. You see that photo is plural. Ember by itself looks for the plural of the resource.

    App.Router.map(function() {
        this.resource('photo', {path: "/photo/:photo_id"});
        this.resource('user', {path: "/user/:user_id"});
    });

A route begins with the first letter of the Resource capitalized and should be in the App namespace. Also, add the word “Route” after the resource’s name. So for the photo resource the route should be like this: App.PhotoRoute

It should also extend the Route object.

    App.PhotoRoute = Ember.Route.extend({});

The Route Object can have different hooks for different things. Two of those hooks are used for defining the Controller name for that resource and defining the Model. Let’s stick with the model.

    App.PhotoRoute = Ember.Route.extend({
        model: function(params){
            return this.store.find('photo', params.photo_id);
        }
    });

Inside, we have specified the model hook and passed a parameter. Where does this parameter go? The photo resource has a url with a parameter: /photo/:photo_id. photo_id is stored in params and can be used inside the function. Don’t forget that every resource and every route has access to the Store. The Store object saves all the info inside it and uses Local Storage for better performance. That way, it cuts down the number of requests on the server. That’s why developing with Ember speeds up your application – in the end, the users are happier.

By using store.find('resource') you can retrieve all the data for this resource from the store object. You can also retrieve only one row. For example, if you want to receive only a photo with a given id, use the store object and find the photo resource with the given id as a second parameter.

    return this.store.find('photo', params.photo_id);

Ember searches for the data in example.com/api/v1/photo_id . By default, Ember works with the data by looking for ids. If you have inserted some relations for this resource, then you can also retrieve the data associated with it. That’s all the code for the routes, very similar for each case and straightforward:

    App.IndexRoute = Ember.Route.extend({
        model: function(){
            return this.store.find('photo');
        }
    });
    
    App.PhotoRoute = Ember.Route.extend({
        model: function(params){
            return this.store.find('photo', params.photo_id);
        }
    });
    
    App.UserRoute = Ember.Route.extend({
        model: function(params){
            return this.store.find('user', params.user_id);
        }
    });

A quick note: the IndexRoute is a default Route, linked with the root URL. And by root I mean the example.com/ URL. There are other default Routes, like ApplicationRoute that executes as the application starts.

The Model Object

Inside Ember’s Model Object, you specify the data and its type of resource. A nice feature of Ember is that when the value of a resource is changed and another value depends on the changed value, it automatically gets updated via some observer magic. A model should start with a capitalized letter and should extend the Model Object.

    App.Photo = DS.Model.extend({});

Inside that Object you should specify all the fields and other values that depend on those core values. You can also add Relations inside the model.

The photo model should look something like this:

    var attr = DS.attr;             // This cuts my writting. Inside the model i use attr instead of DS.attr
    
    App.Photo = DS.Model.extend({
        user_id: attr("number"),    // The expected value is a number
        url: attr("string"),        // The expected value is a string
        title: attr("string"),
        description: attr("string"),
        category: attr("number"),
    
        fullUrl: function(){        // Another value that depends on core values.
            return "/files/" + this.get("url");
        }.property('url'),
    
        backgroundImage: function(){// This depends on another value but not on core ones
            return 'background: url("' + this.get("fullUrl") + '") no-repeat; ';
        }.property('fullUrl')
    
    });

With attr (DS.attr) you specify how you want this data to arrive. For example, we want the user_id value to be a number. This way, we are secured from outside data.

The User Model is similar. Remember, Ember Data will look for it in /api/v1/users. The naming convention is a bit tricky. For example, if you request a resource named user, Ember Data will look for example.com/prefix/users, and if you request a particular resource then it requests example.com/prefix/users/user_id. Knowing how Laravel exposes the data and how Ember wants its data can save you from headaches.

    App.User = DS.Model.extend({
        name: attr("string"),
        lastname: attr("string"),
        username: attr("string"),
    
        fullname: function(){
            return this.get('name') + " " + this.get('lastname');
        }.property("name", "lastname")
    });

Views

Before jumping into templates, I suggest using the Ember Inspector to view the state of your application. There you can find the Routes, Views and Controllers. You can also find the relations between the Controllers and Routes. Take some time to look around with the Inspector, it’ll be of great help later on when you develop your own Ember apps.

Do you remember the first template we wrote in the third part? That’s the application template. That template will be rendered when example.com is accessed in the browser.

You can’t develop the application further if you don’t make a modification inside that template. Replace <!-- The content will be here --> comment with: {{outlet}}.

Why? All our resources are nested inside the application route. But if I look at my code I see no Index on the Router. Why is that?

By default the example.com/ url is assigned to IndexRoute unless you’ve assigned that URL to another route. Ember puts the application onto the top level by default and everything is nested inside it. If you request a URL inside that application route, then by using {{outlet}} as a placeholder, Ember takes that route’s template and puts it inside that placeholder.

Lets make another template and use it for the IndexRoute. This will be the first page. The first template is the app template. The index template will be rendered inside the application’s {{outlet}}.

data-template-name is the name of the template. All the code inside that script tag will be placed inside the {{outlet}}.

    <script type="text/x-handlebars" data-template-name="index">
        <ul class="small-block-grid-1 medium-block-grid-2 large-block-grid-3 custom-grid-ul">
            {{#each}}

                <li {{bind-attr style="backgroundImage"}}>
                    <div class="custom-grid">
                        {{#link-to 'photo' this}}<h5 class="custom-header">{{title}}</h5>{{/link-to}}
                        <span>Author: {{user_id}}</span>
                    </div>
                </li>

            {{/each}}
        </ul>
    </script>

{{#each}} is something like a loop. If the model of the template has an array and we want to query for all the data, then we use this special tag. This loop starts with {{#each}} and ends with {{/each}}. Inside this loop, we use all the values that are returned from the loop. Remember that inside the model we returned the resource photo. The model retrieves the data from the Store and returns it to the template. Look at the Photo model. We specified some fields there and those fields are being used inside the template, inside the {{#each}} loop.

Another special tag is the {{#link-to}} tag. This tag generates a link to the photo route and passes a parameter. The this parameter is the id of that object. In this case the photo id. Again, the {{#link-to}} tag ends with {{/link-to}}. {{title}} isn’t a special tag, it merely retrieves the title value for that object.

Lets add the photo template. This template is the template for the Photo Route. Again, I suggest to see the naming conventions for how this is mapped and how the naming is done.

    <script type="text/x-handlebars" data-template-name="photo">
        <div style="text-align: center;">
            <h4>{{title}}</h4><br>
            <img {{bind-attr src="fullUrl" alt="title"}}><br>
            <span>Author: {{#link-to 'user' user_id}}{{author.name}}{{/link-to}}</span>
        </div>
    </script>

By using the {{attribute-here}} tag, the selected attributes will be generated inside this tag. We have used it inside an <img> tag. Using {{title}} inside a tag as an attribute causes problems. Handlebars and Ember generate some extra objects inside the DOM. To solve this problem, we use {{bind-attr}} instead. When we make a link to the user route, we pass a parameter: the user_id. By clicking the link, the URL will be updated with example.com/user/the_id. But we don’t have a user template yet. Let’s create one.

    <script type="text/x-handlebars" data-template-name="user">
        <h2>Hello: {{fullname}} </h2>
    </script>

This displays only the full name. fullname is a property of our App.User that extends DS.Model.

Before wrapping it all up, I made a gif of how it looks:

enter image description here

Wrapping up

As you can see, this is not a completed project yet. A lot of work is still needed; go ahead and experiment with it, learn from it and change it. The full project will be hosted on my Github account and will be updated frequently. Any contribution is welcome, I’d love to work together.

In this series we learned a lot – I learned a lot too. We saw how to work with the cloud, learned about its good sides and bad sides. We saw how we could develop an application in both environments and how to configure Laravel for different environments. We saw how to build a REST API with Laravel by staying on the same page of an application with Ember. I hope you all had as much fun as I have.

What do you think? Do you want to see more on Heroku, Laravel or Ember? Leave a comment below, it’s always good to hear feedback from the readers!

REST App with Laravel and EmberJS

<< Build REST Resources with Laravel

Free JavaScript: Novice to Ninja Sample

Get a free 32-page chapter of JavaScript: Novice to Ninja

  • WooDzu

    Nice read! I’ve started looking at different front-end frameworks recently for an ambitious webapp. Got Angular, Backbone and Ember compared and ended up with Ember which seems to be most full-blown and stable but hell difficult to learn due to its conventions. I really hope the time spent on it will pay back in the future

    • Aleksander Koko

      Thanks WooDzu. When I first saw ember, I looked at different tutorials. The first tutorial of ember started with the sentence: “I don’t want to lie, Ember is difficult”. You know when you see to learn something and they say it is easy, it’s fast to learn and these kind of things. But no, it was the first time someone told me something is difficult.

      Titanium works with backbone. Backbone was one of the first front-end frameworks there. Angular has a lot out there and it is pretty good. My opinion is go with Ember. It depends on the use case. In some cases you can do something faster with BackBone. Sometimes with Angular. But remember the one thing that is one of the best on Ember. The adaptors. If you learn how to use them and to edit them, then Ember is the best you can use for big web apps.

  • davertMik

    Ok, thanks, now I know why shouldn’t I do that with Laravel.

    One of the most important roles on backend is to produce valid JSON data for EmberData.
    The way your JSON is formed is really ugly, especially compared to how this can be done in ActiveModel Serializers. And what especially looks bad is the way controller is written with all those try-catch-finally blocks.
    I don’t see ANY benefit in using Laravel here (oh maybe just Eloquent).

    • Aleksander Koko

      You can do the backend with anything you want. I choose Laravel because it’s easy, I already know Laravel and it is very popular. You can do Apis with C,C++ if you want but please don’t blame the tool why it doesn’t work as you want. If you want to do that thing, fork it and improve it. Tools aren’t awesome, they are not perfect. After all they are just tools.

      You are free to use anything you want. Btw the JSON is valid for EmberData.

  • ankit

    Good article aleksander,i have one doubt —> i a want to use ember.js for my blogging website but i dont know wether it supports advertising ,i am confused because for every different page we are making a different template so how we can add a normal code from adsense or buyselladds …can we add that code directly as we do in normal html page or there is something which i am missing.

  • Bràñsøñ G Kürīå

    is this on github?….share link please