Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query Params Reset to Default Values on Initial Render If transitionTo is called in beforeModel #12169

Closed
raytiley opened this issue Aug 21, 2015 · 49 comments
Assignees

Comments

@raytiley
Copy link
Contributor

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#

@pixelhandler
Copy link
Contributor

@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?

@rwjblue
Copy link
Member

rwjblue commented Aug 23, 2015

@raytiley - Do you think this is related to #12107 (root issue wise)?

@raytiley
Copy link
Contributor Author

@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(

finalizeQueryParamChange(params, finalParams, transition) {
) 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 :(

@raytiley
Copy link
Contributor Author

@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.

@noslouch
Copy link

+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?

@trek trek self-assigned this Aug 26, 2015
@kmiyashiro
Copy link

@noslouch You could also try the routerTransitions queue.

@kmiyashiro
Copy link

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)

@Glennvd
Copy link

Glennvd commented Oct 27, 2015

@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)

@chrism
Copy link

chrism commented Nov 12, 2015

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

@GCheung55
Copy link
Contributor

Happening on [email protected] for me.

@ZackMattor
Copy link

Happening here too... [email protected]

@ZackMattor
Copy link

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

@taras
Copy link
Contributor

taras commented Dec 23, 2015

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.

@taras
Copy link
Contributor

taras commented Dec 23, 2015

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');
  }
});

@kmiyashiro
Copy link

@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');
    });
}

@taras
Copy link
Contributor

taras commented Jan 12, 2016

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

@kmiyashiro
Copy link

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:

#10577
#12102
#10945

@taras
Copy link
Contributor

taras commented Jan 12, 2016

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

@rzurad
Copy link

rzurad commented Jan 12, 2016

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)

@rzurad
Copy link

rzurad commented Jan 12, 2016

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;
    }
});

@marinatedpork
Copy link

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

@jamesarosen
Copy link

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.

@slbug
Copy link

slbug commented Feb 16, 2016

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

@Charizard
Copy link
Contributor

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

Ember: 2.2.0

@stevesims
Copy link

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

rwjblue pushed a commit that referenced this issue Apr 3, 2016
@rwjblue
Copy link
Member

rwjblue commented Apr 3, 2016

Fix is included in v2.4.4.

@john-kurkowski
Copy link

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.

@raytiley
Copy link
Contributor Author

raytiley commented Apr 4, 2016

@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
Copy link

@raytiley here ya go: https://ember-twiddle.com/0c7e72433e1a3149a7304007fbbd1a54

@raytiley
Copy link
Contributor Author

raytiley commented Apr 7, 2016

@john-kurkowski I think this will fix that error: #13273

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

@JackEllis
Copy link

Still experiencing issue on Ember 2.6.0

@mschinis
Copy link

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

@Unnumbered
Copy link

@mschinis read this: #13600

@mschinis
Copy link

mschinis commented Aug 2, 2016

Awesome thanks @Unnumbered

@PhilLehmann
Copy link

Still happening to me on 2.7.0.

@pauldoerwald
Copy link

pauldoerwald commented Nov 27, 2016

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.

@sdzyba
Copy link

sdzyba commented Jan 14, 2017

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.

@absemetov
Copy link

Same problem in 2.10.0

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

@GCheung55
Copy link
Contributor

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

@InvasiveLionfish
Copy link

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

@supersabillon
Copy link

Also seeing this error on 2.14.

@jorblume
Copy link

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.

@elgordino
Copy link

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

@josemariaruiz
Copy link

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

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

@dmuneras
Copy link

dmuneras commented Oct 1, 2018

Hey!

I can confirm that @jorblume workaround works in Ember 2.18

Best regards,
Daniel.

@puwelous
Copy link

puwelous commented Oct 11, 2018

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.

@raido
Copy link
Contributor

raido commented Nov 4, 2018

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.

@dodeja
Copy link

dodeja commented Nov 13, 2018

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

@patrickberkeley
Copy link

Seeing this in ember 3.26.1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests