Coming from the Backbone world the last several years, I wanted to experiment with Ember. I figure I should try creating a mobile-web app, since I've done a lot of work in the mobile web w/ Backbone. I'm trying to build a global nav bar which has a Save button that shows up on certain routes, and the problem I'm stuck on is how to bind/teardown that click event on the Save button in the nav bar to the controller of the current route.
I have a /routines/new
route where the app will show the user a form to create a new routine. The navigation bar extends Ember.Evented
and propagates the click event via the trigger
call. In my App.RoutinesNewRoute
, within the didTransition
/willTransition
events I'm manually doing the binding from App.NavigationController
to my App.RoutinesNewController
(which contains the logic of how to save the model).
My current approach works, but this means in every route I'll have to manually do the binding/teardown, and it seems like a lot of boilerplate. I'm also envisioning controllers/components that can take advantage of this automatic binding/teardown process that have no direct relationship with the current route (i.e. controllers being inserted via render
calls in the templates). I feel like there should be event that I can hook into where the controller "comes into view" so I can write the binding/teardown in one place and not have to think about it, but maybe I'm not understanding the "Ember" way.
So my question: what's the best way to bind/teardown events from a global controller (i.e. nav bar) to any arbitrary controller that's backing the current views in DOM?
Below is the code:
Right now, I have a App.NavigationController
and an App.NavigationView
that gets rendered into the application layout:
Navigation Controller
App.NavigationController = Ember.ObjectController.extend Ember.Evented,
isShowing: false
showBack: false
title: ""
rightButtonTitle: ""
navigationStyle: (->
if @get('isShowing')
""
else
"display: none;"
).property('isShowing')
backStyle: (->
if @get('showBack')
""
else
"display: none;"
).property('showBack')
rightButtonStyle: (->
if @get('rightButtonTitle')
""
else
"display:none"
).property('rightButtonTitle')
actions:
rightButtonClick: ->
@trigger 'right-button'
backButtonClick: ->
@trigger 'back-button'
reset: ->
@setProperties
isShowing: false
title: ""
rightButtonTitle: ""
App.NavigationView:
App.NavigationView = Ember.View.extend
templateName: 'navigation'
actions:
rightButtonClick: ->
@get('controller').send('rightButtonClick')
backButtonClick: ->
@get('controller').send('backButtonClick')
application.hbs
{{render 'navigation'}}
{{outlet}}
App.RoutinesNewRoute
App.RoutinesNewRoute = Ember.Route.extend
model: ->
if routine = @controllerFor('application').get('routine')
routine
else
@store.find('routine').then (routines) =>
nextDay = routines.content.length + 1
promise = @store.find('exercise').then (exercises) =>
exercises = exercises.map (e) ->
id: e.get('id')
name: e.get('name')
weight: e.get('weight')
routine = @store.createRecord 'routine',
exercises: exercises
day: nextDay
@controllerFor('application').set('routine', routine)
routine
actions:
didTransition: ->
@_super()
# This is where I do all the manual event binding
@controllerFor('navigation').on 'right-button', @, =>
@controller.save().then =>
@controllerFor('application').set('routine', null)
@transitionTo('routines')
@controllerFor('navigation').on 'back-button', @, =>
@transitionTo('routines')
@controllerFor('navigation').setProperties
isShowing: true
showBack: true
title: "New Routine"
rightButtonTitle: "Save"
willTransition: ->
# This is where I do all the manual event teardown
@_super()
@controllerFor('navigation').off 'right-button', @
@controllerFor('navigation').off 'back-button', @
@controllerFor('navigation').send('reset')
App.RoutinesNewController
App.RoutinesNewController = Ember.ObjectController.extend
save: -> @get('model').save()
actions:
save: -> @save()