Home

Awesome

Ember Master Tab

Synopsis

This addon provides a service that allows you to run code on a single tab of your ember application. You might find this useful if your app has functionality that for some reason does not make sense or it is wasteful/redundant to have it run on every tab that is currently open. For example, you might be continuously pulling some state from your server API that you are saving on localStorage which you then use to update your UI through event listeners.

Compatibility

Notes

ember install ember-master-tab

Code Example

You can clone this repository and have a look at the dummy app to see it in action.

run(func1, options = {}).else(func2)

// services/server-time-run.js
import Ember from 'ember';

export default Ember.Service.extend({
  masterTab: Ember.inject.service(),
  currentTime: null,
  init() {
    this._super(...arguments);
    window.addEventListener('storage', e => { // only slave tabs will receive this event
      if (e.key === 'current-time-run') {
        this.set('currentTime', e.newValue);
      }
    });
    this._updateTime();
  },
  _updateTime() {
    Ember.run.later(() => {
      this.updateTime();
      this._updateTime();
    }, 900);
  },
  updateTime(force = false) {
    this.get('masterTab')
      .run(() => {
        Ember.$.getJSON('/api/current-time').then(data => { // will only run on the master tab
          const currentTime = data.currentTime;
          this.set('currentTime', currentTime);
          localStorage['current-time-run'] = currentTime;
        });
      }, { force })
      .else(() => {
        // Master tab is handling it.
      });
  }
});

Notes:

lock(lockName, func1, options = {}).wait(func2)

// services/server-time-lock.js
import Ember from 'ember';

export default Ember.Service.extend({
  masterTab: Ember.inject.service(),
  currentTime: null,
  init() {
    this._super(...arguments);
    this._updateTime();
  },
  _updateTime() {
    Ember.run.later(() => {
      this.updateTime();
      this._updateTime();
    }, 900);
  },
  updateTime(force = false) {
    this.get('masterTab')
      .lock('server-time', () => {
        return Ember.$.getJSON('/api/current-time').then(data => { // will only run on the master tab
          const currentTime = data.currentTime;
          this.set('currentTime', currentTime);
          return currentTime; // will be passed to slave tabs
        });
      }, { force })
      .wait(currentTime => { // will only run on slave tabs; currentTime is the result from the master tab
        this.set('currentTime', currentTime);
      });
  }
});

Notes:

isMasterTab event

Whenever a tab is promoted to master status, the masterTab service will emit an isMasterTab event. So, following the theme of the previous examples, you could also work with EventSource objects (or WebSocket, etc.) like this:

// services/server-time-sse.js
import Ember from 'ember';

export default Ember.Service.extend({
  masterTab: Ember.inject.service(),
  currentTime: null,
  init() {
    this._super(...arguments);
    if (this.get('masterTab.isMasterTab')) {
      this.setup();
    }
    this.get('masterTab').on('isMasterTab', isMaster => {
      if (isMaster) {
        this.setup();
      }
    });
    window.addEventListener('storage', e => {
      if (e.key === 'current-time-sse') {
        this.set('currentTime', e.newValue);
      }
    });
  },
  setup() {
    const sse = new EventSource('/sse');
    sse.onmessage = e => {
      this.set('currentTime', e.data);
      window.localStorage['current-time-sse'] = e.data;
    };
    this.get('masterTab').on('isMasterTab', isMaster => {
      if (!isMaster) {
        sse.close();
      }
    });
  }
});

Notes:

License

Ember Master Tab is released under the MIT Licencse.