Ember.js: Query Params Reset to Default Values on Initial Render If transitionTo is called in beforeModel

Created on 21 Aug 2015  ·  48Comments  ·  Source: emberjs/ember.js

Hopefully the title and this JS bin say it all. The TL;DR; is if you call transitionTo in beforeModel on an initial load (hitting the refresh button) query params will be reset to there default state.

http://emberjs.jsbin.com/kicoro/1#

Bug Query Params

Most helpful comment

Also seeing this error on 2.14.

All 48 comments

@raytiley I'm wondering if this is a bug that exists on both 1.13 and 2.0. Also does the example work on a prior version of Ember, i.e. 1.12?

@raytiley - Do you think this is related to https://github.com/emberjs/ember.js/issues/12107 (root issue wise)?

@rwjblue It might be the same, I really need to dig a little bit deeper to understand where the desired behavior should be happening. The underlying issue is that the qpMeta for each handler haven't been updated before the new transition starts. This happens in the finalizeQueryParamChange(https://github.com/emberjs/ember.js/blob/c9bbfb6081e6c145132addda84ed385b940979ff/packages/ember-routing/lib/system/route.js#L762) event which gets triggered by the router.

My basic understanding without writing a test is this. You initially enter the route with a url with query params... this creates a new transition, but never calls finalizeQueryParamChange because it is interrupted by the transitionTo call in one of the routes hooks. This new transition prepares the queryParams (https://github.com/emberjs/ember.js/blob/master/packages/ember-routing/lib/system/router.js#L603) but since the qpMeta was never updated by the original transition it uses all the default values. Therefore your stuck with your default values :(

@pixelhandler I haven't tested other versions yet, but I don't think this area of the code has changed drastically, except for @trek's recent work on moving queryParam config to the router. I don't believe that has landed yet, but might be a nice place to change this behavior.

Ideally I think we need to finalize and queryParam changes for the current transition when calling transitionTo during an active transition. Since you can pass queryParams in to the transitionTo it still gives the new transition a way to override any queryParam changes of the previous transition call.

For anyone who is following along, as a workaround I was able to schedule the transitionTo onto the run loop in afterRender in order to keep my queryParams. You get an annoying flash of the undesired route, but its better than loosing all the incoming state. I only had this in a few places in my app, so its not a huge priority for me too get this fixed soon, just wanted to report it.

+1

@raytiley I tried your workaround but my queryParams are still getting stripped like so:

Ember.run.scheduleOnce('afterRender', this, function() {
    this.replaceWith('campaign.show.onestep', { queryParams });
});

That's the correct syntax, yes?

@noslouch You could also try the routerTransitions queue.

I do not have this exact issue, but I do have issues with transitionTo({queryParams: { foo: 'bar' }}); inside beforeModel on initial load. I just started getting the an error in 2.1, it was fine in 2.0. Somehow, my query params are persisted and the app behaves as expected, the only weird thing is this error.

From phantom:

TypeError: undefined is not an object (evaluating 'handlerInfos[handlerInfos.length - 1].name')

From chrome:

TypeError: Cannot read property 'name' of undefined
    at _emberRuntimeSystemObject.default.extend.actions.finalizeQueryParamChange (ember.debug.js:25996)
    at Object.triggerEvent (ember.debug.js:28253)
    at Object.trigger (ember.debug.js:50891)
    at finalizeQueryParamChange (ember.debug.js:50040)
    at Object.Router.queryParamsTransition (ember.debug.js:49418)
    at Object.getTransitionByIntent (ember.debug.js:49331)
    at Object.Router.transitionByIntent (ember.debug.js:49436)
    at doTransition (ember.debug.js:50008)
    at Object.Router.transitionTo (ember.debug.js:49505)
    at _emberRuntimeSystemObject.default.extend._doTransition (ember.debug.js:27978)

@kmiyashiro +1, I have exactly the same problem after upgrading to 2.1.0

TypeError: Cannot read property 'name' of undefined
    at _emberRuntimeSystemObject.default.extend.actions.finalizeQueryParamChange (ember.debug.js:25996)
    at Object.triggerEvent (ember.debug.js:28253)
    at Object.trigger (ember.debug.js:50891)
    at finalizeQueryParamChange (ember.debug.js:50040)
    at Object.Router.queryParamsTransition (ember.debug.js:49418)
    at Object.getTransitionByIntent (ember.debug.js:49331)
    at Object.Router.transitionByIntent (ember.debug.js:49436)
    at doTransition (ember.debug.js:50008)
    at Object.Router.transitionTo (ember.debug.js:49505)
    at _emberRuntimeSystemObject.default.extend._doTransition (ember.debug.js:27978)

This has just bitten me as well, if I add this to a completely unrelated route

import Ember from 'ember';

export default Ember.Route.extend({
  beforeModel: function() {
    this.transitionTo('works');
  }
});

Then my query params seem to get stripped and I get this error...

TypeError: Cannot read property 'name' of undefined
    at _emberRuntimeSystemObject.default.extend.actions.finalizeQueryParamChange (ember.debug.js:25680)
    at Object.triggerEvent (ember.debug.js:27911)
    at Object.trigger (ember.debug.js:52341)
    at finalizeQueryParamChange (ember.debug.js:51490)
    at Object.Router.queryParamsTransition (ember.debug.js:50868)
    at Object.getTransitionByIntent (ember.debug.js:50781)
    at Object.Router.transitionByIntent (ember.debug.js:50886)
    at doTransition (ember.debug.js:51458)
    at Object.Router.transitionTo (ember.debug.js:50955)
    at _emberRuntimeSystemObject.default.extend._doTransition (ember.debug.js:27636)

I'm using Ember: 2.3.0-canary+3486f33c

Thanks

Happening on [email protected] for me.

Happening here too... [email protected]

I'm with @kmiyashiro, everything seems to be working as expected... but i'm still getting the error in console. I can easily recreate it in a twiddle. Just load the following with the console open. You can see the error in ember 2.1 and 2.2, doesn't seem to appear in 2.0.

https://ember-twiddle.com/86849f6610f3a68a9840?route=%2F%3Ftest%3Dfoo

Here is a work around that you can use to trigger transitions while this bug remains unresolved.

let RedirectAfterDidTransition = Ember.Mixin.create({
  redirectAfterDidTransition(...args) {
    this.router.one('didTransition', ()=>{
      this.router.transitionTo(...args);
    });
  }
});

export default Route.extend(RedirectAfterDidTransition, {
  afterModel() {
    this.redirectAfterDidTransition('index');
  }
});

I cloned the above Twiddle and added the example https://ember-twiddle.com/2d49a9d1aa3ddaff6ef5?numColumns=1&route=%2F%3Ftest%3Dfoo

Note: this will wait for the transition to finish before doing the redirect.

Here is a simpler version that seems to work as well in my code

import Ember from 'ember';

// This mixin provides `abortAndTransitionTo` method that'll abort the current
// transition and transition to specified parameters.
// It takes same arguments as Route#transitionTo
// it's necessary because of https://github.com/emberjs/ember.js/issues/12169
let AbortAndTransitionTo = Ember.Mixin.create({
  abortAndTransitionTo(...args) {
    this.router.router.activeTransition.abort();
    this.router.transitionTo(...args);
  }
});

export default Route.extend(AbortAndTransitionTo, {
  afterModel() {
    this.abortAndTransitionTo('index');
  }
});

@taras doesn't this waste a fetch since it's after the model?

My workaround is just to catch the error and log it since I couldn't find any side effects.

beforeModel(transition) {
  this._super(...arguments);

  // Logic to set presetParams (dynamic initial params)

  this.transitionTo({ queryParams: presetParams })
    .catch(() => {
      log('Ember 2.1 query param issue: https://github.com/emberjs/ember.js/issues/12169');
    });
}

@kmiyashiro what do you mean: "waste a fetch"? and what is it about your solution that prevents a fetch from being wasted?

I'm assuming that you are fetching (find/query) in model, so if your query params refresh the model, it would re-fetch the model with the updated params. If you use transitionTo inside beforeModel, it implicitly aborts the active transition. Unfortunately, there are _many_ bugs around transitioning to new query params in any of these hooks:

https://github.com/emberjs/ember.js/issues/10577
https://github.com/emberjs/ember.js/issues/12102
https://github.com/emberjs/ember.js/issues/10945

We might have a different use case. If you would like to explore this further, you can ping me on Ember Community slack or we can pair via http://j.mp/EmberSherpa

Encountering this same issue, though i'm not explicitly calling transitionTo or replaceWith, nor is the route doing anything with Ember-Data or any kind of model fetching logic. I'm simply navigating directly to a route with a query param, which maps to a boolean property on the route's controller. This only started happening when I upgraded to Ember 2.1+. The transition seems to complete and everything works fine, but an error is dumped into the console. I've tried a few of the solutions above, as well as overriding some private prototype methods on the Router, but because the error is coming from router.js, which is hidden in closures via the Ember Router, I can't reliably suppress the error.

Since I can't reliably suppress the error, I can't upgrade to Ember 2.1 or 2.2, since this error causes some of my unit tests to break, which my CI system doesn't like.

TypeError: Cannot read property 'name' of undefined
    at _emberRuntimeSystemObject.default.extend.actions.finalizeQueryParamChange (ember.debug.js:25264)
    at Object.triggerEvent (ember.debug.js:27476)
    at Object.trigger (ember.debug.js:51925)
    at finalizeQueryParamChange (ember.debug.js:51074)
    at Object.Router.queryParamsTransition (ember.debug.js:50452)
    at Object.getTransitionByIntent (ember.debug.js:50365)
    at Object.Router.transitionByIntent (ember.debug.js:50470)
    at doTransition (ember.debug.js:51042)
    at Object.Router.transitionTo (ember.debug.js:50539)
    at _emberRuntimeSystemObject.default.extend._doTransition (ember.debug.js:27201)

I was able to suppress the error by monkey-patching the Router:

export default Ember.Router.extend({
    _isRouterPatched: false,

    // monkey-patch the Ember.Router to suppress the error thrown by this bug:
    // https://github.com/emberjs/ember.js/issues/12169
    startRouting() {
        let result = this._super.apply(this, arguments);

        if (!this.get('_isRouterPatched')) {
            let _proto = Object.getPrototypeOf(this.router),
                _original = _proto.queryParamsTransition;

            _proto.queryParamsTransition = function () {
                try {
                    return _original.apply(this, arguments);
                } catch (e) {
                    console.warn(
                        'Ember 2.1 queryParams bug:',
                        'https://github.com/emberjs/ember.js/issues/12169',
                        e
                    );
                }
            };

            this.set('_isRouterPatched', true);
        }

        return result;
    }
});

Happening to me on Ember 2.2.2. I am catching and logging the error as there are no side-effects.

It's perhaps not the most elegant solution, but this seems to work on Ember 1.13:

// app/instance-initializers/global-query-params.js

import Ember from 'ember';
import _ from 'lodash';

const RouteSupport = Ember.Mixin.create({
  transitionTo: superWithGlobalQueryParams,
  replaceWith: superWithGlobalQueryParams
});

function getQueryParams(...names) {
  return names.reduce(function(result, name) {
    const regex = new RegExp(`[?&]${name}=([^?&]+)`);
    const md = regex.exec(window.location.search || '');
    if (md) { result[name] = md[1]; }
    return result;
  }, {});
}

function superWithGlobalQueryParams(...args) {
  const globalQueryParams = getQueryParams('tango-version');

  if (args.length === 1) {
    // transitionTo('some.route');
    args.push({ queryParams: globalQueryParams });
  } else if (args.length === 2 && args[1].queryParams) {
    // transitionTo('some.route', { queryParams: { ... } });
    args[1].queryParams = _.extend({}, globalQueryParams, args[1].queryParams);
  } else if (args.length === 2) {
    // transitionTo('some.route', someModel);
    args.push({ queryParams: globalQueryParams });
  } else {
    // transitionTo('some.route', someModel, { queryParams: { ... } });
    args[2].queryParams = _.extend({}, globalQueryParams, args[2].queryParams);
  }

  return this._super(...args);
}

export default {
  name: 'global-query-params',

  initialize() {
    Ember.Route.reopen(RouteSupport);
  }
};

If you don't want to maintain the list of allowed query-params there, you might be able to just parse all of them using something like this.

Same happens without directly calling transitionTo

beforeModel(transition) {
  let token = transition.queryParams.token;
  return this.get('session').authenticate('authenticator:custom', token).then(...).catch(...);
}

ember-simple-auth tries to transition to '/' if auth successful.

Ember 2.3.0.beta2

Happening to me too. Using @taras's abortAndTransitionTo solution.

Ember: 2.2.0

I'm seeing a variant on this bug in my code.

The error I'm seeing happens when I re-load a page that has query params. The page itself is in a nested route - the setupController methods in the parent and grand-parent routes call transitionTo in order to go to the correct route. The routing transitions work fine when visiting the root-URL of the app - the transitions happen, and the URL gets set correctly including the query params, and no error occurs.

The specific error I see is:

TypeError: Cannot set property '_qpDelegate' of undefined
    at _emberRuntimeSystemObject.default.extend.actions.finalizeQueryParamChange (ember.debug.js:26398)
    at Object.triggerEvent (ember.debug.js:28636)
    at Object.trigger (ember.debug.js:53210)
    at finalizeQueryParamChange (ember.debug.js:52359)
    at Object.Router.queryParamsTransition (ember.debug.js:51737)
    at Object.getTransitionByIntent (ember.debug.js:51650)
    at Object.Router.transitionByIntent (ember.debug.js:51755)
    at doTransition (ember.debug.js:52327)
    at Object.Router.transitionTo (ember.debug.js:51824)
    at _emberRuntimeSystemObject.default.extend._doTransition (ember.debug.js:28347)

The undefined object in question is controller (fetched from route.controller in the loop that's going over qpMeta.qps inside finalizeQueryParamChange.

When I put in a breakpoint I can see that finalizeQueryParamChange gets called twice. It's only on the first time through that the controller object ends up being undefined.

The exception thrown does seem to be harmless, so for me @rzurad's patch works around this issue.

Ember: 2.4.2

Fix is included in v2.4.4.

Hmm, after upgrading "ember" to "2.4.4" in my bower.json, and removing taras's AbortAndTransitionTo workaround, I still have the TypeError: Cannot read property 'name' of undefined error. Same stacktrace as this comment.

@john-kurkowski can you try to get a simple ember-twiddle / jsbin reproduction together? Now that all this code is fresh in my mind I can probably figure it out pretty quick with a reproduction.

@john-kurkowski I think this will fix that error: https://github.com/emberjs/ember.js/pull/13273

Hoping to finish up testing it tomorrow and then I'll pester @rwjblue for a merge :)

Still experiencing issue on Ember 2.6.0

Hmm, I'm on 2.4.6 and still getting the issue.

Awesome thanks @Unnumbered

Still happening to me on 2.7.0.

Still happening to me on 2.9.0. An afterModel() transitionTo(another route) worked fine, but a transitionTo(this-route-with-different-query-params) failed with the TypeError: Cannot read property 'name' of undefined. Using the redirect() method yielded the same result, as does replaceWith() in place of transitionTo(). In order to get it to work, I had to set the route queryParams hash as follows:

queryParams: {
  category: {
    refreshModel: true
  }
}

but that's painful because I don't actually want to refresh the model. (the model I have is perfectly fine!) But with this change all variations of the code above (redirect() and replaceWith()) work fine, although replaceWith() doesn't actually replace the history item.

Still happening to me on 2.10.2 but with a bit different stack trace:

TypeError: Cannot read property 'name' of undefined
    at Class._queryParamsFor (ember.debug.js:27449)
    at Class.finalizeQueryParamChange (ember.debug.js:25269)
    at Router.triggerEvent (ember.debug.js:27862)
    at trigger (ember.debug.js:55572)
    at finalizeQueryParamChange (ember.debug.js:57560)
    at Router.queryParamsTransition (ember.debug.js:56907)
    at Router.getTransitionByIntent (ember.debug.js:56816)
    at Router.transitionByIntent (ember.debug.js:56925)
    at doTransition (ember.debug.js:57527)
    at Router.transitionTo (ember.debug.js:57001)

I suppose this issue should be reopened.

Same problem in 2.10.0
afterModel(model) { if (model.get('length') === 0) { this.transitionTo('users', { queryParams: { page: 1 }}); } }

I'm seeing the same stack trace as @slava-dzyba on 2.10.2 as well.

Seeing the same error in 2.12.1. Tried the above workarounds and proposed fixes but to no avail.

Also seeing this error on 2.14.

My workaround for this is to manually abort the transition before transitioning into another route.

  beforeModel(transition) {
    // For example, first time we transition
    if (transition.sequence === 0) {
      transition.abort();
     // replace with whatever route/query params work for you
      this.replaceWith('index');
    }
  }

Seems to work on Ember 2.18 and 3.0 versions. Hope this helps.

Seeing the same problem with 2.18 and the stack from @sdzyba

I've tried the transition.abort() work around from @jorblume but to no avail, the execution appears to stop and never executes the transitionTo

Same problem with 3.3 when using transitionTo() in afterModel(), my solution:

            if (transaction.sequence === 0) {
                transaction.abort();                
                this.transitionTo('route', {queryParams: newQueryParams});    
            }

Hey!

I can confirm that @jorblume workaround works in Ember 2.18

Best regards,
Daniel.

I solved it by returning just right after this.transitionTo() was called:

model(){
...
if(someCondition){
   return this.transitionTo({ queryParams: error.data.params });
}
...
}

If return statement is on a line below this.transitionTo(), the problem preserves.
Btw, I didn't use transition.abort() at all because it stops any other upcoming transitions - the app gets stuck.

I hit same error but in my case I had to replace usage of this.replaceWith('/'); with actual route name this.replaceWith('my.route') after adding nested route for X route like:

this.route(..., { path: '/' }, function() {
   this.route(..., { path: '/' })
});

After adding the nested route with / app started to crash with above evaluating 'handlerInfos[handlerInfos.length - 1].name' error.

Ember.js v3.5.0.

Any update on this? Haven't been able to get any of the workarounds to work. On Ember v3.4.0

Was this page helpful?
0 / 5 - 0 ratings