Orchestrating child controllers


#1

I could do with some help with a parent/child controller design which requires some event-based communication:

I’m making a website for DJ radio show mixes. I have a Broadcast controller, a Broadcast model and Broadcast template, a Mix model, a Mix controller and a Mix template. The Broadcast model hasMany Mix models. Each Mix model has a Soundcloud ID attribute.

I would like the Broadcast controller to display details of each Mix, and on page-load play the first mix (ordered by ID) using the Soundcloud API. When the mix has finished playing, I want the Mix controller to notify the Broadcast controller that it has finished. Then the Broadcast should instruct the next Mix controller to play.

I can use the Soundcloud JavaScript SDK to play the audio and send an “onfinish” event. I only want a route for the Broadcast - the individual Mixes do not need to be URL-addressable.

What is the best way to design this? I seem to have got most of the way but cannot work out how to get the Broadcast controller to communicate to specific child Mix controllers to tell them to play their audio. Or am I over-architecting things, and should just handle all the audio transitions in the same Broadcast controller?

Here’s the basic structure:

###router.js

this.resource('broadcasts', function() {
   this.resource('broadcast', { path: '/:broadcast_id' })
}

###models/broadcast.js

broadcast = DS.Model.extend({
  session_name: DS.attr('string'),
  broadcast_date: DS.attr('date'),
  mixes: DS.hasMany('mix', { async: true})
});

###models/mix.js mix = DS.Model.extend({ name: DS.attr(), soundcloudId: part: DS.attr(), broadcast: DS.belongsTo(‘broadcast’, { async: true}), });

###controllers/broadcast.js

currentMix: null

###controllers/mix.js

  current: false,
  currentMix: function(){
    return (this.current);
  }.property('current')

###templates/broadcast.js {{#each mix in model.mixes itemController=“mix” }} {{ render ‘mix’ mix }} {{/each}}

###templates/mix.js

<div {{bind-attr class="currentMix"}}>
  <p>{{mix.name}}</p>
  <!-- soundcloud embed here -->        
</div>

#2

Given that your mix is not a route I would suggest you switch your mix to a component. Then all you need to do is sendAction(‘done’) from your mix.

If you want the mix to be part of the route (/broadcasts/:id/mixes/:id) you could also architect it so that mix is a nested route of broadcast and utilize action bubbling to get mix actions to your broadcast route. I’ve found the name controller to be very misleading in ember, the router behaves more like a controller in many ways. Also with the controller going away in ember 2.0 and being replaced by a routable component I think it reinforces the notion of pushing more of what could be considered “controller” functionality into the route.


#3

that makes sense. but how can my Broadcast controller communicate directly with one of its child components? When it receives one Mix’s “done” action, how can it instruct the next Mix in its hasMany relationship to play its audio?

thanks, paul


#4

I assumed you had only one mix component visible. But I realize that you have an each loop in your broadcast template. The easiest way is probably to add a model attribute active / playing and then toggle that from the broadcast since it has access to the mix model.