I need recomendations about emberjs nature

I’m working with emberjs during some time and now I want to redefine my understanding of ember’s nature.

Question 1. Am I right that the route’s model() hook is the best place to write asynchronous code (to get all needed data)? Should I try to place all my network requests (or at least most of them) inside routes’ models?

Question 2. The component hooks have synchronous nature. Does that mean it’s a bad desition to write async code inside hooks?

Say, I have async init() hook where some data is calculated and didRender() hook where I expect to use that data. When ember calls init() it returns Promise, so it’s moved from a stack to a special queue and ember doesn’t wait until event loop returns that code back to a stack. So ember runs next hooks, and when didRender() is being executed the init() hook may not be fulfilled and the expected data may not exist. Is it right?

Question 3. Services hooks should also be synchronous. Because when a service is injected inside a component and is used ember also doesn’t wait until the async hook is fulfilled.

Say, I have a shopping cart service with products property. The products ids are stored in localstorage and I want to get those products from a server to set them into products property.

import Service from '@ember/service';
import { A } from '@ember/array';
import { inject as service } from '@ember/service';

export default Service.extend({
    store: service(),

    init(...args) {
        this._super(args);
        this.set('products', A());
    },

    async loadProducts() {
        const cartProducts = A();
        const savedCartProducts = localStorage.getItem('cartProducts');
        if (savedCartProducts) {
            const parsedSavedCartProducts = JSON.parse(savedCartProducts);
            const ids = Object.keys(parsedSavedCartProducts);
            if (ids.length > 0) {
                const products = await this.store.query('product', {id: Object.keys(parsedSavedCartProducts)});
                products.forEach(p => {
                    p.set('cartQuantity', Number(parsedSavedCartProducts[p.id].qty));
                    cartProducts.pushObject(p);
                });
            }
        }
        this.products.pushObjects(cartProducts);
    },
  ...
});

If I call loadProducts() from service’s init() I can’t use this.cart.products in controllers/application.js , for example. Because service is ready, but async init() is still executed. So, should I call it in routes/application.js model() hook?

Question 4. If there is some general component that doesn’t refer to any route, but this component needs some data from a server. Where should I make async requests? Or computed properties and observers are the only solutions?

Thanks a lot.

1 Like

Hi Alexandr and welcome! Great questions. I don’t have all the answers but can point to some sources I’ve found helpful.

Exactly right. There are a handful of things ember does by default with routes and their templates so you don’t have to write the behavior yourself. Route’s block on the Promise resolving so sometimes you may need to break up the responses if you want to achieve skeleton loading. While it is the default and suitable in many cases your applications needs may differ.

It isn’t bad but there are tradeoffs to be aware of. I’d recommend reading Readers' Questions - "Is it bad to load data in components?" to understand the problem so you can figure out which approach suits your needs best. That may also help answer Question 4.

I think most of the component lifecycle hooks like didUpdateAttrs can be incredibly helpful for responding to the asynchronous changes without needing to know how the fetching of that data works.

I may be missing something about what you are asking with component hooks having a “synchronous nature”. Some do happen in a predictable order but others happen in response to events like when component attributes change. Did the answer above help or should we refine?

Thanks a lot. The link you’ve provided is also very useful.

1 Like