I am trying to implement an application-level error handler to catch failed promises within my application. Most of the suggestions I've seen around this have to do with error logging, but I actually want to trigger a modal window to let my users know they're no longer connected to the internet + can only read data.
I'd like to trigger an action from within the RSVP.onerror
handler. The only way I've managed to do this so far is with something like this:
Ember.RSVP.configure('onerror', function(e) {
window.App.__container__
.lookup('controller:application')
.send('showModal', 'disconnected');
});
but it seems a bit hacky to me.
Is there somewhere more appropriate to write this code? More generally, are there any best practices around notifying users of a lost internet connection?
Ember has a built in error route (docs here).
Depending on how you want to handle other kinds of errors in your app, you could add views/error.js
and run a method on willInsertElement
in that view that tests whether the user is offline and, if true, sends the showModal
action.
Alternatively, failed promises can automatically be caught in the original promise and trigger some method that has been injected into the controller or whatever instance is making the promise:
Controller:
this.set('thing').then(function() {
// Success
}, this.offlineError()); // Failure
Initializer (note the syntax is for ember-cli):
export default {
name: 'offline-error',
initialize: function(container, app) {
var offlineError = function() {
// or something like this
container.lookup('controller:application').send('showModal', 'disconnected');
};
app.register('offlineError', offlineError, { instantiate: false });
// Inject into routes, controllers, and views.
Em.A(['route', 'controller', 'view']).forEach(function(place) {
app.inject(place, 'offlineError', 'offlineError:main');
});
}
};
The above code makes the offlineError method available in all routes, controllers, and views but the beauty of it is that the method has access to the container without using the hacky private property.
I ended up wrapping Offline.js in a service. I'm using ember-cli.
// services/connection.js
/* global Offline */
import Ember from 'ember';
// Configure Offline.js. In this case, we don't want to retry XHRs.
Offline.requests = false;
export default Ember.Object.extend({
setup: function() {
if (Offline.state === 'up') {
this._handleOnline();
} else {
this._handleOffline();
}
Offline.on('down', this._handleOffline, this);
Offline.on('up', this._handleOnline, this);
}.on('init'),
_handleOffline: function() {
this.set('isOffline', true);
this.set('isOnline', false);
},
_handleOnline: function() {
this.set('isOnline', true);
this.set('isOffline', false);
}
});
I injected the service into controllers and routes:
// initializers/connection.js
export default {
name: 'connection',
initialize: function(container, app) {
app.inject('controller', 'connection', 'service:connection');
app.inject('route', 'connection', 'service:connection');
}
};
Now in my templates, I can reference the service:
{{#if connection.isOffline}}
<span class="offline-status">
<span class="offline-status__indicator"></span>
Offline
</span>
{{/if}}
(Offline.js also has some packaged themes, but I went with something custom here).
Also, in my promise rejection handlers I can check if the app is offline, or if it was another unknown error with the back-end, and respond appropriately.
If anyone has any suggestions on this solution, chime in!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With