Ember Substates are great, sometimes . . .

Ember Substates are a great way of rendering a template while some data is being loaded. We could render a fullscreen spinner or even a skeleton version of our route without the need for more code.

By simply creating a new template and appending [routename]_loading, the template will automatically be rendered when the routes model is being fetched.

routes/books.es6  
templates/books.handlebars  
templates/books**_loading**.handlebars  

I was working on a project where the initial query was heavy. Using Ember Substates seemed like a great idea until I realised the route also updated the model with query parameters. It looked weird having the fullscreen loader flash on and off when the model was being updated on the same route.

problem Ember Substates don't work as well when a route also updates the model without transitioning away from the route.

A project I was working on had a route that would update the model when the search was updated or a page was changed. The controller would debounce the query, which would update the route, which would update the model. Using the Loading Ember Substate would cause the loading template to flash on and off anytime the model was being updated, which looked shitty!

problem I want a full screen loading UI which shows when entering a route, but not when the routes model is being updated.

The code I shipped didn't end up using Ember Substates but solved the problem I was facing.

import Route from 'ember-route';  
import on from 'ember-evented/on';  
import injectService from 'ember-service/inject';

export default Route.extend({  
  fromOwnRoute: false,
  fullscreenLoader: injectService(),

  model() {
    return {long_model_request}
  },
  afterModel() {
    this.set('fromOwnRoute', true)
    this.set('fullscreenLoader.show', false)
  },
  beforeModel() {
    if(!this.get('fromOwnRoute')) {
      this.set('fullscreenLoader.show', true)
    }
  },
  didLeaveRoute: on('deactivate', function() {
    this.set('fromOwnRoute', false)
  })
})

The fromOwnRoute property is defaulted to false. When the route is first accessed the beforeModel() hook is called which sets the fullscreenLoader.show property to true.

When the model is returned the afterModel() hook is called which sets fromOwnRoute to true and hides the fullscreenLoader.

When the model is updated, fromOwnRoute is true. This skips setting the property on fullscreenLoader.show to true.

Finally when we leave the route, the deactivate event is called which resets the 'fromOwnRoute' to false and the process is repeated.

improvements

This could be refactored so that the index route is resourced in a way that lets us render the loading substate into the outlet of the parent route.

What other cool techniques do you have for loading states within your application?