Non-standard way of using ember components

Trick that I’m going to talk about may look obvious for experienced developers, but I hope this post will be useful for someone new to it.

If you developed one or two ember¬†components¬†or read official guide, you know that component has lifecycle hooks which¬†can be used to manipulate DOM and integrate components with jquery plugins (usually¬†didInsertElement¬†and willDestroyElement¬†used). I’d say that this (integration with jquery plugins) is a most common usecase of components. But did you ever think about using a component for something other than creating new UI elements?

The thing is, inside didInsertElement (and in willDestroyElement, too) you can manipulate any DOM elements,¬†including those that are outside of your application’s templates. For example, you can:

  • Use a component to add/remove css class on body element. Useful for theming or when you want to change a look of application completely for certain routes.
  • Use a component to add/remove css class from root element of application.
    import Ember from 'ember';
    
    export default Ember.Component.extend({
        parentClass: '',
        didInsertElement() {
            this.$().parent().addClass(this.get('parentClass'));
        }
    });

    Just drop this component in application.hbs, somewhere in the beginning of template

  • Use a component to remove some elements after ember application is loaded. Ember-load (a¬†simple loading indicator, while your static assets download, and your Ember.js app starts up)¬†uses¬†this method to remove loading animation.

In all these cases component’s template is empty, so nothing (only empty div) will be rendered.

Of course,¬†it’s a good practice to change only component’s own DOM in lifecycle hooks. But in examples above it works very well.

How to: Upload files with Ember.js (creating your own uploader)

Recently I faced a common task: upload user files (photos) with some data (newly created record) attached. So I needed an ajax uploader with following features:

  • Uploading few files in one request
  • Sending data together ¬†with files
  • Complete and error events that will be triggered just once for the whole request, not for every single file
  • Preview for images
  • Client-side validation
  • Send data even without files
  • Customizable template

First, I googled for existing solutions. Tried to use some addons, than tried to wrap one of existing uploaders¬†into a component. But I didn’t found any¬†that fits¬†my needs. Some of them are not¬†free for commercial use, some are¬†unable to load all files with one request, some doesn’t provide a complete event for all files. After a day of struggle I decided to create¬†my own component for file uploads and now I want to share my experience. Uploader will work in all modern browsers, including IE10+. IE9 is not supported, as it is incompatible with HTML5 File API.

You can get an app with implemented uploader on github. Below you can find a brief explanation.

(more…)

How to: Handle backend errors in Ember.js properly

Today I want to talk about a very interesting topic: how to handle errors that happens on backend. These may include database errors, validation errors, server faults, etc. There are two major situations when an error may occur, and these situations should be handled differently.

An error during a transition into a route (i.e. in  a model hook)

When Ember.js performs a transition to a new route, it calls a model method of that route. Here is a code of a typical route:

//..app/routes/posts.js
import Ember from 'ember';

export default Ember.Route.extend({
  model: function () {
    return this.store.findAll('post');
  }
});

In this example¬†(given that post¬†model is defined, and backend¬†is up and running), Ember will make a GET¬†request to /posts¬†and then render¬†a posts¬†template with some data. But what if server will not respond or will respond with an error? A transition will fail and user either will stay on a previous page or will see a blank page (if they¬†came directly to route with error). In both cases they¬†will not understand what is going on and will become irritated. For these kind of situations¬†it would be great to¬†create a nice error page with an error message and a “Try again” button on it.

(more…)

JavaScript tip: an easy way to copy objects

Let’s say you have some object, and want to made a “backup” before modifying it. The easiest way to do this is to use JSON.stringify and JSON.parse. First function serializes an object into json string, and the second one parses json string into object. Be aware that only properties will be copied and not methods. Example:

var original = {
    name: "John",
    secondName: "Doe",
    has: ["house", "car", "yacht"],
    doSomething: function() {}
};
var backup = JSON.parse(JSON.stringify(original));

original.name = "Ivan";
original.secondName = "Ivanov";

console.log(original);
console.log(backup);

As caniuse.com reports, this feature is available ¬†in all modern browsers, so it’s safe to use.

Ember.js tip: A helper to combine conditions in {{#if}} blocks

If you tried Ember.js, you may have noticed that it is not possible to use logical operators in templates. For example, such template will not work:

{{#if (param1 || param2)}}
  Will not work
{{/if}}

Instead, you supposed to combine param1 and param2 into a computed property (param3) in your controller (component, model) and use this computed property in your template:

import Ember from 'ember';

export default Ember.Controller.extend({
  param3: Ember.computed('param1', 'param2', function () {
    return this.get('param1') || this.get('param2');
  })
});
{{#if param3}}
  Will work
{{/if}}

Quite a lot of coding for such simple task, isn’t it? Surely, this method helps to keep templates simple and it is good if you need such logic just in one-two places. But what if you need to combine conditions often? Or do it in {{#each}}¬†block?¬†A helper will help.

First, create a file /path-to/app/helpers/logical-or.js. You may run ember generate helper logical-or to make ember-cli generate it for you. Put following lines in this file:

//..app/helpers/logical-or.js
import Ember from 'ember';

export function logicalOr(params) {
  var result = false;
  for (var i = 0; i < params.length; i++) {
    result = result || !!params[i];
  }
  return result;
}

export default Ember.Helper.helper(logicalOr);

Now you can use your new helper in any template:

{{#if (logical-or param1 param2)}}
  Will work
{{/if}}

If you need, you may pass more than 2 parameters:

{{#if (logical-or param1 param2 param3 param4 param5)}}
  Will work
{{/if}}

You may also create logical-and helper (as a replacement for &&) and even combine them in templates.

That’s all. Have a nice code!