Skip to content

Instantly share code, notes, and snippets.

@alanpeabody
Last active October 28, 2020 23:33
Show Gist options
  • Star 20 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save alanpeabody/1c2c23293e3d02b1cee1 to your computer and use it in GitHub Desktop.
Save alanpeabody/1c2c23293e3d02b1cee1 to your computer and use it in GitHub Desktop.
Ember Best Practices

Ember Best Practices

Some thoughts and ideas on best practices building Ember apps after 2 years building and maintaining 6+ apps. This is less about the obvious best practices, like use ember-cli, and more along the lines of when to use what technique. As with every best practice there are exceptions to every rule.

Ember data

Ember data relationships

Routing

The empty main route

Ember apps tend to be great use cases for apps with tons of interaction, often apps like this require authentication to access much (if not all the app).

router.js

Router.map(function() {
  this.route('main', {path: ''}, function() {
    // All your authenticated routes
    this.route('dashboard');
    this.route('profile');
  });
  // All your non authenticated routes
  this.route('sign-in');
  this.route('register');
  this.route('help');
});

The main route, because it's path is empty, does not add any unwanted layers in your url structure. However it gives you two major benefits:

  1. The main route can enforce the authentication requirement, and if desired can resolve the current user in the model hook.
  2. The main route becomes a shared template that can contain your primary application navigation, layout, etc.

Auth routes/shared templates

Modal routes

Components

Prefer components over views

This is really just emphesising the Ember core team's statmentes, but one place I see this used a lot is in the view paired with each template/controller/route.

For example, when calling a jquery hook or starting some sort of css transition the built in view is often used:

app/views/profile.hbs

export default Ember.View.extend({
  fadeIn: function() {
    // Fade logic.
  }.on('didInsertElement');
});

A better alternative is converting that view into a component, which can be used anywhere in any template:

app/templates/profie.hbs

{{#fade-in duration=500}}
  <!-- Existing template -->
{{/fade-in}}

Using a component for this has a few benefits:

  1. More DRY. Even if you abstract your view to a mixin, you still have to create a view, import the mixin and maintain it. With a component you maintain one component and the very simple, obvious template code.
  2. More explicit. Keeping the component explicit in the template makes the intended (non standard) behavior explicit, not hidden in a view, potentially with a hidden mixin.
  3. More flexible. Using a component makes it way easier to re-impliment the logic else where. In this example we can set smaller fragments impliment the same fade behavior.

Prefer components over helpers

Prefer component actions over controller actions

A component is an encapsulation of functionality and presentation. Components should be composed to build larger pieces of functionality, with each compontent handling as much of it's own behavior as possible.

For example:

app/templates/profile.hbs

{{#fade-in duration=500}}
  {{image-upload image=currentUser.avatarUrl autosave=model}}
  {{user-form user=currentUser}}
{{/fade-in}}

In this example the image-upload component would upload the new image, update the currentUser, then save the user the the server. You could additionally pass in success/failure text as optional arguments.

The user-form also is responsible for setting fields (via form input bindings most likely) and saving the user to the server. This may seem like you now have two components triggering save calls, which isn't as DRY, however you have completely re-usable components and have eliminated coupling between the component and the context in which they are used. This gives you numerous maintainability benefits.

Prefer components over item controllers

Much like the above, if you need an item controller, you are trying to encapsulate one model/record with a template and behavior. A component combines that much more elegantly and cleanly.

Saving from components

Do it.

What actions to send from components

Component should still send actions up the chain, however they should be actions relevant to the application state, not a particular model's state. Things like transitioning routes, opening/closing modals, reloading data into the store, etc are good canidates for pushing to the controller or route action handling.

Use components all the time.

Any block of related markup and content, especially if it exposes some sort of user interaction, should be a component.

{{main-nav}}
{{user-nav}}
{{user-avatar user=user}}
{{user-form user=user}}
{{login-form}}
@martin-hoger
Copy link

Thank you very much for the notes, I have found it very useful, mainly the point "The empty main route".

I just have a question concerning "Saving from components: Do it". I like this idea, it looks like it makes things more simple, however it seems to violate the data down actions up principle. Are there some drawbacks when saving at the component?

@aswinjose89
Copy link

Expecting more code difference in performance and coding standard perspective like angular style guide https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment