Ember router with transient states - ember.js

Inspired by Emberjs: Conditional redirect in router I thought I could use transient states in Ember router, as that is what the 'index' route in that question is - it is entered and immediately transitions to another state without a new event triggering it.
I can get the desired effect using the following code (action triggering transitionTwice), however the URL is updated in reverse order leaving it at the first state even though the App traverses through the states in the correct order ending in the expected last state.
App.Router = Ember.Router.extend({
enableLogging: true,
root: Ember.Route.extend({
transitionTwice: Ember.Route.transitionTo('twiceStep1'),
index: Ember.Route.extend({
route: '/',
}),
twiceStep1: Ember.Route.extend({
route: '/twiceStep1',
connectOutlets: function (router) {
router.transitionTo('twiceStep2');
}
}),
twiceStep2: Ember.Route.extend({
route: '/twiceStep2',
})
})
}),
App.initialize();
The console output shows the correct state changes:
STATEMANAGER: Sending event 'transitionTwice' to state root.
STATEMANAGER: Entering root.twiceStep1
STATEMANAGER: Entering root.twiceStep2
However the URL ends up in:
...test2.html#/twiceStep1
Can anyone spot anything I am doing wrong?
Thanks,
Jon.

I have been able to get this to work successfully by wrapping the second transitionTo() call in Ember.run.next().
Ember.run.next(function () {
router.transitionTo('twiceStep2');
});
The problem is that for any transition the URL is updated after the connectOutlets() call completes. This means that chained calls to transitionTo from connectOutlets nest and the URL is updated in reverse order as the calls complete.
While this fixes my immediate problem I'm not sure this really constitutes a final answer to the question as this implies that the final state needs to have the knowledge that it will be entered from a previous, transient state - which isn't good separation. But I thought I should record it in case anyone else struggles with this anomaly.

Related

Query-params-new nested routes strange error

In an application I use the latest canary versions for Ember and Ember Data. I have the following router:
this.resource('articles', {path: '/articles'}, function() {
this.resource('article', {path: '/:article_id'});
});
In the ArticlesController I specify some queryParams:
queryParams: ['category', 'search'],
category: '1', // defaults to 1
searchTerm: "",
In my ArticlesRoute I specify a model refresh and the model:
queryParams: {
category: {
refreshModel: true
}
},
model: function(params) {
// here I do use the params to return articles based on category and/or searchTerm
}
So far so good, all code above works perfect. However when I do a this.transitionTo('article', articleObject) or this.transitionToRoute('article', articleObject) in my application I get the following error:
Error: You didn't provide enough string/numeric parameters to satisfy all of the dynamic segments for route article
at Object.__exports__.default.subclass.createParamHandlerInfo (http://localhost:8000/vendor/ember/index.js:44242:21)
at Object.__exports__.default.subclass.applyToHandlers (http://localhost:8000/vendor/ember/index.js:44121:37)
at Object.__exports__.default.subclass.applyToState (http://localhost:8000/vendor/ember/index.js:44088:21)
at Object.Router.transitionByIntent (http://localhost:8000/vendor/ember/index.js:43312:33)
at Object.Router.refresh (http://localhost:8000/vendor/ember/index.js:43459:21)
at EmberObject.extend.refresh (http://localhost:8000/vendor/ember/index.js:22616:35)
at EmberObject.extend._actions.queryParamsDidChange (http://localhost:8000/vendor/ember/index.js:22328:22)
at Object.triggerEvent (http://localhost:8000/vendor/ember/index.js:24563:38)
at trigger (http://localhost:8000/vendor/ember/index.js:44812:16)
at fireQueryParamDidChange (http://localhost:8000/vendor/ember/index.js:43612:9) index.js:14220
Uncaught Error: Assertion Failed: Error: You didn't provide enough string/numeric parameters to satisfy all of the dynamic segments for route article index.js:3658
This strange error only happens when I first clicked a Category so the queryParam category is changed, and then fire a transitionTo to an article.
I have tried to use the debugger; statement to get the source of the error. However it seems that is an event that calls this error. When searching the sourcecode I found that the error originates from line 44242 of ember.js.
Does anyone know why this error happen after I have transitioned to a non query-params-new route?
Edit: now in Github too: https://github.com/emberjs/ember.js/issues/5070 (comment on Github)
JSBin: http://emberjs.jsbin.com/yiquyupa
I ran into almost the same issue in an afterModel callback where the parent route/controller has queryParams.
I found that if you just pass the queryParams from the transition argument (not the queryParams argument which is null/undefined) into Route.transitionTo / Route.replaceWith then the transition completes.
Example:
afterModel: function(model, transition, queryParams) {
// FIXME: unresolved Ember issue https://github.com/emberjs/ember.js/issues/5070
this.replaceWith('another.route', model, { queryParams: transition.queryParams});
}
I don't know why this happens, but queryParams is still a relatively new feature and still has some rough edges.

Observes other childcontroller?

I am trying to observe another childcontroller.
I have the following router:
this.resource('generics', {path: '/gens'}, function() {
this.resource('generic', {path: '/:generic_id'}, function() {
this.route('prepare'); // Objectcontroller
this.route('sent'); // Objectcontroller
});
});
I have an observer in the sent controller, however it does not work. I have currently: 'controllers.sent.id' to get the id prepared in the prepare controller.
If I do a needs property with generic.prepare. It shows this error:
#needs must not specify dependencies with periods in their names (generic.sent)
I have also tried to use setupController to add the id to the sent controller properties, however the observer is worthless then.
it would be genericPrepare, but prepare/sent should never exist at the same time. You'd probably be better off sending the object to generic and then having sent grab the property off of it. Why are you against setting it up during setupController? The route will always hit setupController of sent before it's visible to the end user.

Parent Route Not Asked to Handle Event?

The guide says that when an action is triggered, Ember first looks for a handler in the current controller, then if it can't find it in the controller it looks in the current route, then the parent route, etc. I'm not seeing that happen.
My routes:
App.Router.map(function() {
// Creates 'products' and 'products.index' routes
this.resource('products', function(){
// ...
});
});
My super trivial products.index template;
<span {{action fooBar}}>Run fooBar</span>
To test this, I'm currently at /#/products in the browser, and Ember logs "Transitioned into 'products.index'" saying I'm currently in the products.index route, as I expect. Now if click on the action, Ember should look for a handler in:
ProductsIndexController
ProductsIndexRoute
ProductsRoute
My observations:
If I put the handler in ProductsIndexController, it works.
If I put the handler in ProductsIndexRoute, it works.
However, if I put the handler in ProductsRoute, it's never called:
.
App.ProductsRoute = Ember.Route.extend({
events: {
fooBar: function(){
alert("alarm!");
}
}
});
Instead I see the error:
*Error: Nothing handled the event 'fooBar'.*
What am I missing?
One of my other javascript files was also setting/creating App.ProductsRoute (and doing nothing with it), which was causing a conflict. Silly mistake.

ember routing not working how I want

I have 3 views in my router.
The index view picks viewtwo as the default.
The problem is when I go to viewone or viewthree it goes through the index route! and loads the viewto before then rerouting to whichever I clicked viewone or viewthree and in my setup that causes some glitch as each view does something to the HTML that it then has to undo before leaving but something is not syncing too well (I tested it has something to do with how fast things load and javascript is applied onto it because my glitch only occurs if I click back and forth really fast).
What is the opposite of didInsertElement right before it gets destroyed?
Also why is the viewto getting loaded from the index route?
index: Ember.Route.extend({
route: '/',
redirectsTo: 'viewtwo'
}),
viewone: Ember.Route.extend({
route: '/viewone',
connectOutlets: function( router ) {
....
viewtwo: Ember.Route.extend({
route: '/viewtwo',
connectOutlets: function( router ) {
....
viewthree: Ember.Route.extend({
route: '/viewthree/:item_id',
connectOutlets: function( router, item ) {
....
What is the opposite of didInsertElement right before it gets destroyed?
willDestroyElement. See this page: http://emberjs.com/api/classes/Ember.View.html#event_willDestroyElement
Also why is the viewto getting loaded from the index route?
The reason index loads viewto is because of the redirect, but I think you already knew that. Not sure what you are asking here.
As far as all your routes going through index, you might want to post a fiddle that emulates this problem. /viewthree should not enter index from what you've posted. If you post a fiddle that can reproduce the problem it will help people provide better answers.

Emberjs: Conditional redirect in router

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.