Question

First off, I'm aware of this question, Display online and offline (e.g. airplane) mode in an Ember.js App but that does not solve all the issues I'm having. The answer to that question uses Heyoffline.js, which is a great library (it's what JSbin uses), but it does not check the connection status on "startup" of an Ember app, only if I turn off my wifi while I'm using the app. I am creating an offline/online compatible Ember app, I just need to know what the status is on startup and I need full control of the "guts", meaning UI, views, etc...Heyoffline.js does too much of that for you.

Very simply put, I need to fire up an Ember app:

window.App = Ember.Application.create({
LOG_TRANSITIONS: true,
LOG_VIEW_LOOKUPS: true,
service: null,
isOffline: function() {
    var networkCheck = navigator.onLine ? true : false;
    return networkCheck;
},
ready: function() {
    this.set('service', App.HeyOffline.create());
    Ember.debug("App ready!");
}
});

App.HeyOffline = Ember.Object.extend({
service: null,
init: function() {
    this.set('service', new Heyoffline({
        noStyles: true,
        prefix: "network",
        text: {
            title: "No Internet Connection",
            content: "Your experience will be limited, it's suggested that you connect to the internet",
            button: "Continue Anyway."
        }
    }));
    this.set('service.options.onOnline', this.offline);
    this.set('service.options.onOffline', this.online);
},
online: function() {
    App.set('isOffline', false);
    Ember.debug('Online');
},
offline: function() {
    App.set('isOffline', true);
    Ember.debug('Offline');
}
});

App.ApplicationRoute = Ember.Route.extend();

view.js

App.NetworkView = Ember.View.extend({
layoutName: "_network_error",
classNames: ['network-error', 'flash-message'],
classNameBindings: ['isHidden:hide:show'],
isHidden: true,
networkFail: function() {
    var self = this;
    console.log("Network Failure");
    this.set('isHidden', false);
    Ember.run.next(function() {
        setTimeout(function(){
            self.set('isHidden', true);
        }, 3000);
    });
},
didInsertElement: function() {
    var self = this;
    Ember.run.next(function() {
        if(App.isOffline) { // <--- THIS ALWAYS RETURNS TRUE, doh
            self.networkFail();
        }
    });
}
});

application.hbs

{{outlet}}
{{outlet modal_dialog}}
{{outlet modal_status}}
{{outlet debug}}

{{#view App.NetworkView }} {{/view }}

There is a lot more to my application than that, but I think I've pulled out all the pertinent parts. I'm not saying it has to be done like this at all, the only requirements I have are:

  1. Has to check and set "something" at startup so I can show a message if needed. I'm not saying it has to deferReadiness or anything, from a UX/UI perspective the message can pop up once the App is "ready" or the run loop has finished.
  2. I have to have control over the message UI and it has to be "Emberized", (I'm thinking a view or component), it's going to be a slide-down from the top element (like Facebook mobile connection warning).
  3. Has to have some sort of listener (if I lose my connection, I can pop up the little message) - Heyoffline.js does this wonderfully
  4. Has to be Emberized - must work "the ember way", maybe Ember.Evented or some Pub/Sub setup. I can hack it in, trying not to do that.
  5. Would love it to be a little more foolproof than window.onLine - that only really checks if you are connected to a LAN or router, not "really" checking internet
  6. I am in a very specific environment, Webkit specifically, I don't need IE or old browser support.
  7. I have no other pre-reqs, I'd be fine trashing my current setup entirely.

Any thoughts, suggestions or nuggets anyone wants to share would be greatly appreciated.

No correct solution

OTHER TIPS

One problem is with:

if(App.isOffline) { // <--- THIS ALWAYS RETURNS TRUE, doh

You should call it as a function, instead of just accessing it. A function is a truthy value, but what you want is the result of the function.

Now, for the rest of the functionality. It makes sense to have encapsulate it on a service and make it available to the rest of your controllers/views. For simplicity, I would make it part of ApplicationController.

App.ApplicationController = Ember.Controller.extend( {
  isOnline: true,  // assume we're online until proven wrong
  init: function () {
    updateNetworkStatus();
  },

  updateNetworkStatus: function () {
    var appController = this;
    if (!navigator.onLine) {
      this.set('isOnline', false);
      return; // return early, no point in pinging the server if we have no LAN
    }
    Ember.$.get('myserver.com/status').done(function () {
      // todo: consider checking the result
      appController.set('isOnline', true);
    }).fail(function () {
      appController.set('isOnline', false);    
    }).always(function () {
      Ember.run.later(appController, 'updateNetworkStatus', 60000);
    });
  } 
});

We still check the navigator.online, but if it's true, we don't assume we have a connection all the way to the server. We check that. Once we get the response we update the flag and keep updating that flag every minute.

From other controllers, we can simply add a dependency to the application controller.

App.OtherController = Ember.Controller.extend( {
  needs: ['application'],
});

And then use it in properties or as a check before doing certain action:

App.OtherController = Ember.Controller.extend( {
  needs: ['application'],
  canEdit: function () {
    return this.get('controllers.application.isOnline');
  }.property('controllers.application.isOnline')
});

Or from your view

{{controllers.application.isOnline}}

Hope this helps. As I said, I would normally encapsulate this into a service that can then later be injected into controllers just to move this type of concerns away from application, but that probably requires answering a second question.

Hope this helps

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top