A Beginner's Journey into Ember.js

One year ago I started my first job in tech as a junior frontend web developer. At this job, I was introduced to the Ember framework and spent most of my time building ambitious web applications. After a while, I really fell in love with Ember so I decided to give a talk about my firsthand experiences learning the framework.

This article is a summary of the talk I gave early in August at the Ember London meetup. The talk was called 'A Beginner's Journey into Ember.js' and I spoke about my experiences learning the Ember framework and highlighted some of the troubles I had early on.

slides-intro

You can also check out the video of the talk up on Vimeo.

Jumping In

I've been taking a placement year as part of my Computer Science degree, and this is how I came to work at Repositive. Repositive is a startup based in Cambridge that is building a web platform to allow genomic researchers to find, share and collaborate on data.

When I joined the team one year ago, our existing web application was built on Ember v1.13. This was my first experience jumping into a large, existing code base and it was quite a challenge familiarising myself with the structure of an Ember project.

talking

Prior to this, I had only built some simple, static sites using Bootstrap and a bit of jQuery, but I had never experimented with frameworks such as Angular, Ember or React. This meant I had a lack of knowledge of JavaScript frameworks and learning Ember was quite a challenge for me.

Some of the things that stuck out for me in the beginning where the 'opinions' and how strong the conventions are. Ember also has a fast release cycle, which means keeping up to date with the latest updates and deprecations was difficult, at first. This definitely improved with the release of the Long-Term Release build channel.

Embracing The Conventions

So, the first thing I had to do when I was learning Ember was to embrace the conventions to fully benefit from the framework's power. This meant understanding the basic flow of an Ember application and understanding what each Ember object is used for.

ember-core-diagram

This diagram shows the basic flow of an Ember application. Everything in Ember starts with the URL which is mapped to a route, or route handler, by the router.js file. The route is then able to call the model hook to allow data to be rendered into the templates. The templates can then render multiple components to make the views more reusable and these components can even call a number of nested components to further improve the code reusability.

Once I had fully grasped this concept, writing Ember applications became a faster process and building fairly complex apps becomes a simpler task.

Another great thing about Ember conventions is that by using the command line tool, Ember CLI, you are able to quickly generate and structure your applications in a really clean and powerful way.

After a while I really began to appreciate the Ember conventions, as it allowed a beginner like me to build complex web applications faster, without having to be an 'expert'.

Route-Driven Development

Another principle I learned over the past year was the idea of 'route-driven development', meaning that you can design and build your applications using the structure of Ember routing. As the routes are able to highlight a particular application state, you are able to understand easily how the application should be built around a set of mockups you receive from your designer.

To give an example of this, I'll use a demo of a simple book listing app that displays book information from a specific author. The below mockups show how the application should be designed in the interface and how the Ember routes can be developed.

book-demo-1

This first mockup shows that we have an 'authors' view which simply lists all authors in the database. From this design, we know that we will need to generate an 'authors' route and build the template to match the mockup.

book-demo-2

Next, we have a view for the list of books for each individual author. From this, we can tell that we need to nest an 'author' route inside of 'authors' and that we will also need to nest 'books' inside of this. We can also set a dynamic segment to display the author's ID in the URL.

book-demo-3

And, finally, we have the individual 'book' route, which displays the information for a single book by a specific author. Again, we already know that we need to generate a nested 'book' route inside of the previous 'books' route and that we can set a dynamic segment to display the route as the books ID in the URL.

So, the way that Ember handles application state is really great because it means that we don't need to make assumptions when deciding how to build the applications around the design, we just know how this should be structured already.

// router.js
App.Router.map(function() {  
  this.route('authors', function() {
    this.route('author', { path: '/:author_id' }, function() {
      this.route('books', function() {
        this.route('book', { path: '/:book_id' });
      });
    });
  });
});

If we take a look at how the router.js would look after these routes are generated, we can see how nice and clean the structure of this is. We are then able to build the templates and controllers for these pages as expected by using the same structure as the routes.

This was one of my favourite things about Ember at the beginning, because it helped me understand how the state of the application was managed and simplified the decisions I had to make when building pages from the designs.

Data Down, Actions Up

Something I really struggled with at the beginning of my Ember experience was the idea of data down, actions up. This is the principle that data should be passed down from the routes to the controllers and then to the components, and actions should be passed up from components to controllers and finally back to the routes.

ddau-diagram

When you have many components, especially when there are several nested layers, this is sometimes hard to keep track of and it's easy to get lost when bubbling actions back up to the controllers.

To give a demo of the problem I faced, I'll take the same example from before and add a star-rating component on top of the book-item component.

book-ddau-demo

From here, the theoretical user should be able to adjust and set a rating, out of five, for each book displayed.

The first thing we need to do is find all book records for the selected author.

// routes/authors/books.js

model: function() {  
  return this.store.findAll('book');
}

Next, we loop over each book and display a book-item component for each book record we have returned. The book model is then passed to the component to allow it to access the data for the book.

<!-- templates/authors/books.hbs -->

{{#each model as |book|}}
  {{book-item book=book}}
{{/each}}

Then, we need to build the actual markup for the book-item component. This will contain an image for the book cover, which links to the book route of a specific ID, and displays a set of five stars. The stars will be displayed as a nested component called star-rating and the rating value is passed in from the book model. We also can set an action called setRating that will allow the user to set the new rating when they click on a star.

<!-- templates/components/book-item.hbs -->  
{{#link-to 'books.book' book.id}}
  <img class="book-cover" src="{{book.coverImage}}">
{{/link-to}}

{{star-rating item=book rating=book.rating on-click=(action "setRating") maxRating=5}}

The action then needs to be handled by the component, so, as I would have expected, you write an action to take the book rating as a parameter, update the value of the rating and save the book model.

// components/book-item.js
export default Ember.Component.extend({  
  actions: {
    setRating(params) {
      const { item: book, rating } = params;
      book.set('rating', rating);
      return book.save();
    }
  }
});

It turns out that this doesn't work because we haven't bubbled the action back up to the controller to handle the value update for the rating. The fix for this is pretty simple, but when you have a large code base with many layers of components, tracking this component hierarchy can be pretty confusing.

What we can do is to send the setRating() action up to the controller and handle the action logic there.

// components/book-item.js
export default Ember.Component.extend({  
  actions: {
    setRating(item) {
      this.sendAction('updateRating', item);
    }
  }
});

Once we've sent the action up, we can then define an updateRating() action to handle the logic and update the rating value as we were doing before.

// controllers/authors/books.js
export default Ember.Controller.extend({  
  actions : {
    updateRating(params) {
      const { item: book, rating } = params;
      book.set('rating', rating);
      return book.save();
    },
  }
});

Now we can now pass the action into the book-item component call as a parameter to allow it to bubble the action up to the controller.

<!-- templates/authors/books.hbs -->

{{#each model as |book|}}
  {{book-list book=book updateRating='updateRating'}}
{{/each}}

And finally, we can see that the user is able to update and save the star rating for all of the book items displayed.

book-demo-gif

The idea of data down, actions up in Ember is a really good practice to get into, but I definitely had some confusion understanding the flow early on, especially when jumping into a large, existing code base. Once I had understood this flow properly, I was able to build better components and write much cleaner code.

Community

One of the best things about being an Ember developer is the community that comes with it. The Ember community is extremely active and inclusive and there is always the opportunity to learn from other developers. I definitely feel like my experience as an Ember developer has been greatly improved by the amount of support I've received from the addon community via the Slack channel and screen-sharing over a Skype call.

Lessons Learned

So, to sum up, some of the important lessons I've learned over the past year are as follows:

  1. Embrace the Conventions
  2. Follow Route-Driven Patterns in Design
  3. Understand the Data Down, Actions Up Flow
  4. Get Involved with the Community

lessons-learned

The Future 🚀

The developments of the Ember framework are happening pretty quickly and it's exciting to follow the progress the core team is making. I'm excited to see how the development of routeable components will impact Ember in the near future. There has also been some pretty great work on the Glimmer 2 rendering engine, which is being built to dramatically improve the template rendering performance.

I'm also excited to continue getting involved with the Ember community and hope to learn even more from future meetups and events.

Closing

It's safe to say, that learning Ember as a junior developer has been a great experience for me and helped me improve my skills. The guides have been a really great resource for me whilst learning, and I definitely recommend that other new Ember developers follow them exclusively.

Originally posted at danielgynn.com.

Read more posts by Daniel Gynn

Front-end developer.