17

We are using version pre4 of ember.

We have a framework (SignalR) working parallel with ember that handles real-time notifications to our application. In the older versions of ember we were able to access the global reference of the router / controller. But with the new version of Ember this is no longer possible. (This is fine) We have tried different approaches like setting up a global controller in the top route:

setupController: function(){
    app.appController = this.controllerFor('app');
}

and sending an event to this controller, which bubbles up to the route like this:

notificator.update = function (context) { 
    app.appController.send('notificationOccured', context);
});

But this feels like working against the Ember team which just removed the global references.

So now to the big question: is there a better way to access the router or a controller from outside Ember? Preferably send an event to either with a context.

All help is appreciated!

TommyKey
  • 568
  • 3
  • 11

2 Answers2

36

So now to the big question: is there a better way to access the router or a controller from outside Ember? Preferably send an event to either with a context.

Yes. This sounds like a good fit for the ember instrumentation module. Have an appropriate controller subscribe to SignalR events, then trigger them whenever your app handles real-time notification.

First, add a method to ApplicationController for processing updates. If not defined here the event would bubble to the router.

App.ApplicationController = Ember.Controller.extend({
  count: 0,
  name: 'default',
  signalrNotificationOccured: function(context) {
    this.incrementProperty('count');
    this.set('name', context.name);
  }
});

Next, setup your ApplicationController by subscribing to the signalr.notificationOccured event. Use the before callback to log the event and send it's payload to the controller.

App.ApplicationRoute = Ember.Route.extend({
  setupController: function (controller, model) {
    Ember.Instrumentation.subscribe("signalr.notificationOccured", {
      before: function(name, timestamp, payload) {
        console.log('Recieved ', name, ' at ' + timestamp + ' with payload: ', payload);
        controller.send('signalrNotificationOccured', payload);
      },
      after: function() {}
    });
  }
});

Then from your SignalR Application, use Ember.Instrumentation.instrument to send payload to your ApplicationController as follows:

notificator.update = function (context) { 
  Ember.Instrumentation.instrument("signalr.notificationOccured", context);
});

I posted a working copy with simulated SignalR notifications here: http://jsbin.com/iyexuf/1/edit

Docs on the instrumentation module can be found here, also check out the specs for more examples.

danb
  • 10,239
  • 14
  • 60
  • 76
Mike Grassotti
  • 19,040
  • 3
  • 59
  • 57
  • 1
    instrument as a name to send a signal is quite confusing in my opinion. What does 'Instrument a block of code by using `Ember.instrument` mean exactly: send signal with a specified name and payload and call the callback at the end ? Btw good catch. – ken Jan 25 '13 at 05:33
  • Sweet, this was perfect! This design is excact what I was looking for. Works like a charm too. – TommyKey Jan 25 '13 at 09:16
  • Mike, this might be just what I'm looking for, but can this be used for general purpose events or only for rendering? Does it only bubble upwards or can any object listen for an event triggered by any other object? Ideally I'd love to be able to duplicate jQuery's $.trigger() which lets you set up bespoke events anywhere, and trigger them FROM anywhere. – commadelimited Mar 12 '13 at 14:39
  • It can be used for general purpose events, not sure what you mean by bubble upwards but instrumentation is not specific to rendering. For sure any object can listen to events triggered by any other object. – Mike Grassotti Mar 12 '13 at 19:02
  • I like this approach but it needs some more work if you want to use it in a more transient controller (i.e. not ApplicationController). In that case we need to take care to also unsubscribe or we'll be delivering events to already destroyed controllers. In our app we ended up subscribing in `activate` hook and unsubscribing in `deactivate` hook. – Damir Zekić Apr 14 '13 at 10:50
  • This works fine but isn't it some find of a hack? There should be some better way to interface with external code. And if there isn't they should add such mechanics because it's s very common scenario when you need to interface with a world beyond ember.js. – petersaints Apr 30 '13 at 01:17
  • Guess there could always be a better way. "interface with external code" could mean a lot of things, don't think there's any one right way to do it. Sometimes using didInsertElement() hook on ember views is all you need. This 'instrumentation' approach makes sense when you're looking for a simple way to pass messages between frameworks. And when your needs are more complicated, look at something like http://oasisjs.com/ – Mike Grassotti Apr 30 '13 at 07:38
  • The only problem I see with this approach is that Instrumentation purpose is not making this kind of things. Other than that this solution is fine for me. – petersaints Apr 30 '13 at 11:18
  • Thanks! One change that I made was that I needed to put my controller action under the `actions` property. I think that changes in Ember a little while back – Brian Underwood Aug 28 '15 at 21:52
3

You probably shouldn't be doing this but here's a way to get access to the application's controllers, views, models and router instances. When your application is initialized, controllers, views, models and router are all registered in the application container __container__

APP.__container__.lookup('controller:foo').get('content');
APP.__container__.lookup('view:foo').get('templateName');
APP.__container__.lookup('router:main');

What i think you should do is encapsulate calls to the 3rd party library inside Ember and let Ember manage the whole application. See this attempt to make JQuery UI ember-aware

ken
  • 3,745
  • 6
  • 34
  • 49
  • Ye, we have thought of using the __container__, but I think it was @wycats that renamed it to __container__ from container so that dev should 'really' understand not to use it :) So I will stay clear of __container__ from now. But I will look at lukes JQuery-UI-Ember attempt. Thanx for the link! – TommyKey Jan 24 '13 at 07:40
  • This works for my purposes, thanks. Is there another way to do this where use of container can be avoided? – Jake Rowsell Feb 16 '15 at 10:07