I'm trying to make use of _super in the handler of a Promise inside of a Controller action, but it doesn't work because it seems to lose the correct chain of functions.
ApplicationRoute = Ember.Route.extend SimpleAuth.ApplicationRouteMixin,
actions:
sessionAuthenticationSucceeded: ->
#get("session.user").then (user) =>
if #get("session.isTemporaryPassword") or not user.get "lastLogin"
#transitionTo "temp-password"
else
#_super()
I want to revert to the Mixin's default behavior on the else but I need to resolve user asynchronously before I can do a conditional statement. I tried:
ApplicationRoute = Ember.Route.extend SimpleAuth.ApplicationRouteMixin,
actions:
sessionAuthenticationSucceeded: ->
_super = #_super
#get("session.user").then (user) =>
if #get("session.isTemporaryPassword") or not user.get "lastLogin"
#transitionTo "temp-password"
else
_super()
and
ApplicationRoute = Ember.Route.extend SimpleAuth.ApplicationRouteMixin,
actions:
sessionAuthenticationSucceeded: ->
#get("session.user").then (user) =>
if #get("session.isTemporaryPassword") or not user.get "lastLogin"
#transitionTo "temp-password"
else
#_super.bind(#)()
Neither works.
This answer claimed this should work as of 1.5.0, but I'm using 1.7.0-beta.5 and it's no go. Is there a way to get this to work, even in terms of approaching this differently?
Ember currently doesn't support calling _super asynchronously. In that example I'm not actually calling _super asynchronously, it's synchronous still.
http://emberjs.com/blog/2014/03/30/ember-1-5-0-and-ember-1-6-beta-released.html#toc_ever-present-_super-breaking-bugfix
In order to continue the bubbling you need to call this.target.send() with the name of the action.
see: How can I bubble up an Ember action inside a callback function?
Something like this should work:
ApplicationRoute = Ember.Route.extend SimpleAuth.ApplicationRouteMixin,
actions:
sessionAuthenticationSucceeded: ->
#get("session.user").then (user) =>
if #get("session.isTemporaryPassword") or not user.get "lastLogin"
#transitionTo "temp-password"
else
#target.send('sessionAuthenticationSucceeded')
Related
I have a route that has an afterModel hook.
afterModel: function(model, transition){
transition.send('doInAppRoute');
}
I have an action in my application route:
doInAppRoute: function(){
var controller = this.get('controller');
controller.set('someProp', true);
}
When I allow the action to bubble from the route with the afterModel hook, I get the following error.
Error while processing route: embed Cannot read property 'set' of undefined TypeError: Cannot read property 'set' of undefined
If I put an action call to doInAppRoute in the application template, everything runs as expected.
If the action call to doInAppRoute bubbles, this.get('controller') in my application route is undefined. Why?
And how can this be changed so the bubbled action updates the application controller property?
Thanks to #torazaburo for leading me in the right direction.
This is what I did.
Set someProp to an initial value in application route.
someProp: false,
Then in the the application route action do something like:
doInAppRoute: function(){
this.set('someProp', true);
}
And then in the application route setupController do:
setupController: function(controller, model){
controller.set('someProp', this.get('someProp'));
controller.set('model', model);
}
Then everything should work.
How is possible, that binding have latency?
App.SomeRoute = Ember.Route.extend
actions:
someAction: ->
console.log #controllerFor('some').get('foo')
Ember.run.later (=> console.log #controllerFor('some').get('foo')), 10
App.SomeController = Ember.ObjectController.extend()
App.OtherController = Ember.ObjectController.extend
needs: ['some']
fooBinding: 'controllers.some.foo'
action:
changeIt: ->
#set('foo', 'bar')
#send('someAction')
If action changeIt is evoked (from view for example) console output will be undefined, but after a little delay it will be bar.
What I am doing wrong?
This is the expected behavior, when you change a bound property, the syncronization isn't performed immediatelly, it's just scheduled and called later.
This is important because if we have a fooBinding: 'controllers.some.foo'. Doesn't matter how many times the foo is changed, the controllers.some.foo property just need to be updated once with the final result. With this, we avoid to trigger uneeded observers, and save processing.
You can force the scheduled syncronization queue to flush using Ember.run.sync(), with the following code:
App.SomeRoute = Ember.Route.extend
actions:
someAction: ->
Ember.run.sync()
# now you can see the updated value
console.log #controllerFor('some').get('foo')
App.SomeController = Ember.ObjectController.extend()
App.OtherController = Ember.ObjectController.extend
needs: ['some']
fooBinding: 'controllers.some.foo'
action:
changeIt: ->
#set('foo', 'bar')
#send('someAction')
Keep in mind that this is just an example, for you see the updated value. The use of Ember.run.sync() isn't recommended.
Using the non-async router, we could expect redirect on a route to be called only after resolving promises on from the model function. That's no longer the case.
How can something like this be implemented today?
App.ClientRoute = Ember.Route.extend
model: (params) ->
App.Client.findById params.client_id
redirect: ->
unless #modelFor 'client'
#transitionTo 'clients'
As of RC6, you would implement this like so:
App.ClientRoute = Ember.Route.extend
model: (params) ->
App.Client.findById params.client_id
afterModel: (resolvedModel)->
unless resolvedModel
#transitionTo 'clients'
those are two Gists from the developer of the new async router of Ember.js which will explain the new behaviour and show you some examples:
https://gist.github.com/machty/5723945
https://gist.github.com/machty/5647589
Hope they'll help you - I'm reading and following through at the moment and I think everything is really well explained ;)
I'm trying to observe the route change to apply some common action once rendered. The idea is to have a feature similar to the onload but as we use a single-page app this needs to be triggered on each route changes. (could be scoped to the new view)
I found how to observe the currentPath changes:
App.ApplicationController = Ember.Controller.extend({
currentPathDidChange: function() {
prettyPrint()
}.observes('currentPath');
});
While this works good in some cases, it gets triggered when the route changes, but still to early to apply content changes as it seem to append before the content gets rendered.
Any idea on the best practice to achieve such goal?
Have you tried deferring the code with Ember.run.schedule? For instance,
App.ApplicationController = Ember.Controller.extend({
currentPathDidChange: function() {
Ember.run.schedule('afterRender', this, function() {
prettyPrint();
});
}.observes('currentPath')
});
Due to the deprecation of Controllers in Ember 1.x finding the url in the router would be a good way to future proof your apps. You can do this in ember-cli like so:
// similar to onLoad event behavior
export default Ember.Route.extend({
afterModel: function (model){
Ember.run.next(() => {
console.log(this.get('router.url'));
});
}
});
// hacky way to get current url on each transition
export default Ember.Route.extend({
actions: {
didTransition: function() {
Ember.run.next(() => {
console.log(this.get('router.url'));
});
}
}
});
This will log: /posts and /posts/3/comments ect.
Is there a way to have a conditional redirect in the Ember.js Router, without breaking internal consistency of the router?
What you could do (as of today), is something like that:
root: Ember.Route.extend({
index: Ember.Route.extend({
enter: function(router) {
var logged = /* get from appropriated source... */;
Ember.run.next(function() {
if (logged) {
router.transitionTo('loggedIn');
} else {
router.transitionTo('loggedOut');
}
});
}
}),
loggedIn: Ember.Route.extend({
// ...
}),
loggedOut: Ember.Route.extend({
// ...
})
})
Do not miss the Ember.run.next as while you are in enter, the state transition is always pending, so you have to transition after that.
We use it as shown for authent, but you could imagine using it for whatever condition you have to...
The new router now includes a
beforeModel
hook which you could over-ride to include conditional logic while transitioning to a route.
The beforeModel hook will be called before the
model
hook is called and it gets passed a
transition
object. You can decide if you want to redirect to another route using
transitionToRoute()
or you could abort the transition if you don't want to redirect by calling
transition.abort()
Depending on what you're trying to do, you may be looking for conditional transitions. This is covered in another stackoverflow question, the TLDR of which is to check this fiddle.