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

Resolves not working when enabling the sticky states plugin #3

Closed
cloudmark opened this issue Mar 1, 2017 · 5 comments
Closed

Resolves not working when enabling the sticky states plugin #3

cloudmark opened this issue Mar 1, 2017 · 5 comments

Comments

@cloudmark
Copy link

cloudmark commented Mar 1, 2017

Description

Resolves throw a DI error when enabling the sticky states plugin. This happens due to the fact that the original PathNode references are being change.

Reproduce

The repository https://github.com/cloudmark/ui-router-stickstates-bug outlines the problem with ui-router-sticky-states transitions. In order to run this demo first install the dependencies using

npm install

and run the code using:

npm run start.deving

As soon as you run the code the first error shows up, this is due to a refactor in ui-router-core:

TypeError: state.parameters is not a function
    at new PathNode (http://localhost:5555/node_modules/ui-router-core/lib/path/node.js:26:38)
    at Function.PathNode.clone (http://localhost:5555/node_modules/ui-router-core/lib/path/node.js:55:16)
    at applyToParams (http://localhost:5555/node_modules/ui-router-core/lib/path/pathFactory.js:99:42)
    at Array.map (native)
    at Function.PathFactory.treeChanges (http://localhost:5555/node_modules/ui-router-core/lib/path/pathFactory.js:108:45)
    at StickyStatesPlugin._calculateStickyTreeChanges (http://localhost:5555/node_modules/ui-router-sticky-states/lib/stickyStates.js:98:60)
    at eval (http://localhost:5555/node_modules/ui-router-sticky-states/lib/stickyStates.js:41:47)
    at TransitionHook.invokeHook (http://localhost:5555/node_modules/ui-router-ng2/_bundles/ui-router-ng2.js:1486:37)
    at eval (http://localhost:5555/node_modules/ui-router-ng2/_bundles/ui-router-ng2.js:1538:53)
    at Array.forEach (native)
    at Function.TransitionHook.runAllHooks (http://localhost:5555/node_modules/ui-router-ng2/_bundles/ui-router-ng2.js:1538:15)
    at new Transition (http://localhost:5555/node_modules/ui-router-ng2/_bundles/ui-router-ng2.js:2731:24)
    at TransitionService.create (http://localhost:5555/node_modules/ui-router-ng2/_bundles/ui-router-ng2.js:6254:16)
    at StateService.transitionTo (http://localhost:5555/node_modules/ui-router-ng2/_bundles/ui-router-ng2.js:6709:56)
    at BaseUrlRule.handler (http://localhost:5555/node_modules/ui-router-ng2/_bundles/ui-router-ng2.js:4918:24)
  -------------   Elapsed: 42 ms; At: Wed Mar 01 2017 22:13:21 GMT+0100 (CET)   -------------  
    at getStacktraceWithUncaughtError (http://localhost:5555/node_modules/zone.js/dist/long-stack-trace-zone.js?1488402794479:33:12) [angular]
    at new LongStackTrace (http://localhost:5555/node_modules/zone.js/dist/long-stack-trace-zone.js?1488402794479:27:22) [angular]
    at Object.onScheduleTask (http://localhost:5555/node_modules/zone.js/dist/long-stack-trace-zone.js?1488402794479:83:18) [angular]
    at ZoneDelegate.scheduleTask (http://localhost:5555/node_modules/zone.js/dist/zone.js?1488402794479:343:51) [angular]
    at Object.onScheduleTask (http://localhost:5555/node_modules/zone.js/dist/zone.js?1488402794479:237:29) [angular]
    at ZoneDelegate.scheduleTask (http://localhost:5555/node_modules/zone.js/dist/zone.js?1488402794479:343:51) [angular]
    at Zone.scheduleTask (http://localhost:5555/node_modules/zone.js/dist/zone.js?1488402794479:192:39) [angular]
    at Zone.scheduleMacroTask (http://localhost:5555/node_modules/zone.js/dist/zone.js?1488402794479:206:25) [angular]
    at http://localhost:5555/node_modules/zone.js/dist/zone.js?1488402794479:1548:33 [angular]
    at setTimeout (eval at createNamedFn (http://localhost:5555/node_modules/zone.js/dist/zone.js?1488402794479:1483:17), <anonymous>:3:37) [angular]
    at uiRouterFactory (http://localhost:5555/node_modules/ui-router-ng2/_bundles/ui-router-ng2.js:8750:5) [angular]
    at AppModuleInjector.get (/AppModule/module.ngfactory.js:229:61) [angular]
    at AppModuleInjector.getInternal (/AppModule/module.ngfactory.js:359:48) [angular]
    at AppModuleInjector.NgModuleInjector.get (http://localhost:5555/node_modules/@angular/core/bundles/core.umd.js:8490:48) [angular]
  -------------   Elapsed: 17 ms; At: Wed Mar 01 2017 22:13:21 GMT+0100 (CET)   -------------  
    at getStacktraceWithUncaughtError (http://localhost:5555/node_modules/zone.js/dist/long-stack-trace-zone.js?1488402794479:33:12) [angular]
    at new LongStackTrace (http://localhost:5555/node_modules/zone.js/dist/long-stack-trace-zone.js?1488402794479:27:22) [angular]
    at Object.onScheduleTask (http://localhost:5555/node_modules/zone.js/dist/long-stack-trace-zone.js?1488402794479:83:18) [angular]
    at ZoneDelegate.scheduleTask (http://localhost:5555/node_modules/zone.js/dist/zone.js?1488402794479:343:51) [angular]
    at Object.onScheduleTask (http://localhost:5555/node_modules/zone.js/dist/zone.js?1488402794479:237:29) [angular]
    at ZoneDelegate.scheduleTask (http://localhost:5555/node_modules/zone.js/dist/zone.js?1488402794479:343:51) [angular]
    at Zone.scheduleTask (http://localhost:5555/node_modules/zone.js/dist/zone.js?1488402794479:192:39) [angular]
    at Zone.scheduleMicroTask (http://localhost:5555/node_modules/zone.js/dist/zone.js?1488402794479:203:25) [angular]
    at scheduleResolveOrReject (http://localhost:5555/node_modules/zone.js/dist/zone.js?1488402794479:677:14) [angular]
    at resolvePromise (http://localhost:5555/node_modules/zone.js/dist/zone.js?1488402794479:638:21) [angular]
    at http://localhost:5555/node_modules/zone.js/dist/zone.js?1488402794479:679:17 [angular]
    at Object.onInvokeTask (http://localhost:5555/node_modules/@angular/core/bundles/core.umd.js:3971:41) [angular]
    at ZoneDelegate.invokeTask (http://localhost:5555/node_modules/zone.js/dist/zone.js?1488402794479:362:36) [angular]
  -------------   Elapsed: 4 ms; At: Wed Mar 01 2017 22:13:21 GMT+0100 (CET)   -------------  
    at getStacktraceWithUncaughtError (http://localhost:5555/node_modules/zone.js/dist/long-stack-trace-zone.js?1488402794479:33:12) [angular]
    at new LongStackTrace (http://localhost:5555/node_modules/zone.js/dist/long-stack-trace-zone.js?1488402794479:27:22) [angular]
    at Object.onScheduleTask (http://localhost:5555/node_modules/zone.js/dist/long-stack-trace-zone.js?1488402794479:83:18) [angular]
    at ZoneDelegate.scheduleTask (http://localhost:5555/node_modules/zone.js/dist/zone.js?1488402794479:343:51) [angular]
    at Object.onScheduleTask (http://localhost:5555/node_modules/zone.js/dist/zone.js?1488402794479:237:29) [angular]
    at ZoneDelegate.scheduleTask (http://localhost:5555/node_modules/zone.js/dist/zone.js?1488402794479:343:51) [angular]
    at Zone.scheduleTask (http://localhost:5555/node_modules/zone.js/dist/zone.js?1488402794479:192:39) [angular]
    at Zone.scheduleMicroTask (http://localhost:5555/node_modules/zone.js/dist/zone.js?1488402794479:203:25) [angular]
    at scheduleResolveOrReject (http://localhost:5555/node_modules/zone.js/dist/zone.js?1488402794479:677:14) [angular]
    at ZoneAwarePromise.then (http://localhost:5555/node_modules/zone.js/dist/zone.js?1488402794479:766:17) [angular]
    at new ApplicationInitStatus (http://localhost:5555/node_modules/@angular/core/bundles/core.umd.js:3342:64) [angular]
    at AppModuleInjector.createInternal (/AppModule/module.ngfactory.js:308:36) [angular]
    at AppModuleInjector.NgModuleInjector.create (http://localhost:5555/node_modules/@angular/core/bundles/core.umd.js:8474:80) [angular]
    at NgModuleFactory.create (http://localhost:5555/node_modules/@angular/core/bundles/core.umd.js:8448:22) [angular]

In order to fix this error update the code in /node_modules/ui-router-core/lib/path/node.js from:

var PathNode = (function () {
    function PathNode(stateOrPath) {
        if (stateOrPath instanceof PathNode) {
            var node = stateOrPath;
            this.state = node.state;
            this.paramSchema = node.paramSchema.slice();
            this.paramValues = common_1.extend({}, node.paramValues);
            this.resolvables = node.resolvables.slice();
            this.views = node.views && node.views.slice();
        }
        else {
            var state = stateOrPath;
            this.state = state;
            this.paramSchema = state.parameters({ inherit: false });
            this.paramValues = {};
            this.resolvables = state.resolvables.map(function (res) { return res.clone(); });
        }
    }

to

var PathNode = (function () {
    function PathNode(stateOrPath) {
        if (stateOrPath instanceof PathNode) {
            var node = stateOrPath;
            this.state = node.state;
            this.paramSchema = node.paramSchema.slice();
            this.paramValues = common_1.extend({}, node.paramValues);
            this.resolvables = node.resolvables.slice();
            this.views = node.views && node.views.slice();
        }
        else {
            var state = stateOrPath.state;
            this.state = state;
            this.paramSchema = state.parameters({ inherit: false });
            this.paramValues = {};
            this.resolvables = state.resolvables.map(function (res) { return res.clone(); });
        }
    }

Note: Don't know if you would prefer if I report this as a separate bug in the ui-router-core project.

Refreshing the console again should now give the DI Error for Transition (what we are after!):

NoProviderError {__zone_symbol__error: Error: DI Error
    at NoProviderError.ZoneAwareError (http://localhost:5555/node_modules/zone.js/di…, _nativeError: ZoneAwareError, keys: Array[1], injectors: Array[1], __zone_symbol__message: "No provider for Transition!"…}

Debugging

Debugging within the stack trace we can see No provider for Transition!.

NoProviderError
__zone_symbol__error
:
Error: DI Error at NoProviderError.ZoneAwareError (http://localhost:5555/node_modules/zone.js/dist/zone.js?1488402794479:958:33) at NoProviderError.BaseError [as constructor] (http://localhost:5555/node_modules/@angular/core/bundles/core.umd.js:1239:20) at NoProviderError.AbstractProviderError [as constructor] (http://localhost:5555/node_modules/@angular/core/bundles/core.umd.js:1365:20) at new NoProviderError (http://localhost:5555/node_modules/@angular/core/bundles/core.umd.js:1405:20) at ReflectiveInjector_._throwOrNull (http://localhost:5555/node_modules/@angular/core/bundles/core.umd.js:2937:23) at ReflectiveInjector_._getByKeyDefault (http://localhost:5555/node_modules/@angular/core/bundles/core.umd.js:2976:29) at ReflectiveInjector_._getByKey (http://localhost:5555/node_modules/@angular/core/bundles/core.umd.js:2908:29) at ReflectiveInjector_.get (http://localhost:5555/node_modules/@angular/core/bundles/core.umd.js:2777:25) at AppModuleInjector.NgModuleInjector.get (http://localhost:5555/node_modules/@angular/core/bundles/core.umd.js:8491:56) at UIInjectorImpl.getNative (http://localhost:5555/node_modules/ui-router-ng2/_bundles/ui-router-ng2.js:2674:43) at getDependency (http://localhost:5555/node_modules/ui-router-ng2/_bundles/ui-router-ng2.js:2639:49) at Array.map (native) at ResolveContext.getDependencies (http://localhost:5555/node_modules/ui-router-ng2/_bundles/ui-router-ng2.js:2645:32) at getResolvableDependencies (http://localhost:5555/node_modules/ui-router-ng2/_bundles/ui-router-ng2.js:2418:42) at ZoneDelegate.invoke (http://localhost:5555/node_modules/zone.js/dist/zone.js?1488402794479:330:26)
__zone_symbol__message
:
"No provider for Transition!"
__zone_symbol__stack
:
"Error: DI Error↵    at NoProviderError.ZoneAwareError (http://localhost:5555/node_modules/zone.js/dist/zone.js?1488402794479:958:33)↵    at NoProviderError.BaseError [as constructor] (http://localhost:5555/node_modules/@angular/core/bundles/core.umd.js:1239:20)↵    at NoProviderError.AbstractProviderError [as constructor] (http://localhost:5555/node_modules/@angular/core/bundles/core.umd.js:1365:20)↵    at new NoProviderError (http://localhost:5555/node_modules/@angular/core/bundles/core.umd.js:1405:20)↵    at ReflectiveInjector_._throwOrNull (http://localhost:5555/node_modules/@angular/core/bundles/core.umd.js:2937:23)↵    at ReflectiveInjector_._getByKeyDefault (http://localhost:5555/node_modules/@angular/core/bundles/core.umd.js:2976:29)↵    at ReflectiveInjector_._getByKey (http://localhost:5555/node_modules/@angular/core/bundles/core.umd.js:2908:29)↵    at ReflectiveInjector_.get (http://localhost:5555/node_modules/@angular/core/bundles/core.umd.js:2777:25)↵    at AppModuleInjector.NgModuleInjector.get (http://localhost:5555/node_modules/@angular/core/bundles/core.umd.js:8491:56)↵    at UIInjectorImpl.getNative (http://localhost:5555/node_modules/ui-router-ng2/_bundles/ui-router-ng2.js:2674:43)↵    at getDependency (http://localhost:5555/node_modules/ui-router-ng2/_bundles/ui-router-ng2.js:2639:49)↵    at Array.map (native)↵    at ResolveContext.getDependencies (http://localhost:5555/node_modules/ui-router-ng2/_bundles/ui-router-ng2.js:2645:32)↵    at getResolvableDependencies (http://localhost:5555/node_modules/ui-router-ng2/_bundles/ui-router-ng2.js:2418:42)↵    at ZoneDelegate.invoke (http://localhost:5555/node_modules/zone.js/dist/zone.js?1488402794479:330:26)"
_nativeError
:
ZoneAwareError
constructResolvingMessage
:
(keys)
injectors
:
Array[1]
keys
:
Array[1]
message
:
(...)
name
:
(...)
originalStack
:
(...)

Stepping through the resolution code one can observe that the subPath in ResolveContext includes a node with name: app.home
even though the current node also has state name app.home (subPath should not include the current node). The subNode is included since the original reference is not the same (even though the properties of the path nodes are the same) and hence return r.token === token returns false.

ResolveContext.prototype.getDependencies = function (resolvable) {
        var _this = this;
        var node = this.findNode(resolvable);
        // Find which other resolvables are "visible" to the `resolvable` argument
        // subpath stopping at resolvable's node, or the whole path (if the resolvable isn't in the path)
        var subPath = PathFactory.subPath(this._path, function (x) { return x === node; }) || this._path;
        var availableResolvables = subPath
            .reduce(function (acc, node) { return acc.concat(node.resolvables); }, []) //all of subpath's resolvables
            .filter(function (res) { return res !== resolvable; }); // filter out the `resolvable` argument
        var getDependency = function (token) {
            var matching = availableResolvables.filter(function (r) { return r.token === token; });
            if (matching.length)
                return tail(matching);
            var fromInjector = _this.injector().getNative(token);
            if (!fromInjector) {
                throw new Error("Could not find Dependency Injection token: " + stringify(token));
            }
            return new Resolvable(token, function () { return fromInjector; }, [], fromInjector);
        };
        return resolvable.deps.map(getDependency);
    };
    return ResolveContext;

Since the subPath includes also the app.home node which has a resolvable requiring the Transition dependency, this resolvable is not included in the list of availableResolvables and hence is not resolved.

So where is the reference being changed? References are changes when trying to compute the active and inactive states in the sticky states code whilst running the simulate transition.

Specifically the line:

 var simulatedTC = ui_router_core_1.PathFactory.treeChanges(inactiveFromPath, tc.to, trans.options().reloadState);

creates shallow clones of the original PathNodes.

The references are swapped in the lines that follow:

tc.reactivating = simulatedTC.retained.slice(tc.retained.length);
// Entering nodes are the same as the simulated transition's entering
tc.entering = simulatedTC.entering;
// The simulatedTC 'exiting' nodes are inactives that are being exited because:
// - The inactive state's params changed
// - The inactive state is being reloaded
// - The inactive state is a child of the to state
tc.exiting = tc.exiting.concat(simulatedTC.exiting);
// Rewrite the to path
tc.to = tc.retained.concat(tc.reactivating).concat(tc.entering);
@christopherthielen
Copy link
Member

christopherthielen commented Mar 5, 2017

@cloudmark thanks for the very detailed bug and instructions. It really helped me track this one down.

I switched the sticky states onCreate hook to a higher priority. Now it processes the treeChanges first, then lets ui-router-core add the coreResolvables such as the Transition object.

I'd also like to clear up some confusion, for posterity:

As soon as you run the code the first error shows up, this is due to a refactor in ui-router-core:
Note: Don't know if you would prefer if I report this as a separate bug in the ui-router-core project.

This was actually due to a problem with the published UMD bundles of ui-router-ng2. We were including all the code from ui-router-core in the UMD bundle, and pointing the package.json main: entry to the bundle. Since you're bundling the es5 code, you got the ui-router-ng2.js file which includes all the code from ui-router-core. However, since sticky states also imports from ui-router-core, it was adding the core code TWICE to your project. This caused the instanceof checks to fail, since some of the PathNode objects came from ui-router-ng2.js and some of them came from ui-router-core.

Unfortunately, the fix involves releasing a new ui-router-ng2 which does not bundle the ui-router-core code in its UMD bundle. This also means you have to add an entry in your systemjs config file for the ui-router-core entry point.

(subPath should not include the current node)

subPath should include the nodes from root up to and including the node being searched for.


In ui-router-core 3.0.0, the Transition token was added after the onCreate hooks. In ui-router-core 4.0+, it is added by https://github.com/ui-router/core/blob/master/src/hooks/coreResolvables.ts. ui-router-ng2 1.0.0-beta.4 uses ui-router-core 4.0.0.

I've released sticky-states 1.2.0 which depends on ui-router-core 3.x-4.x. I'll be releasing 1.3.0 soon which depends on ui-router-core ^5.0.0 and updates to the new apis

@mohanrao
Copy link

@christopherthielen

I am seeing same issue with angular-ui-router 1.0.4 , @uirouter/sticky-states v1.4.1 and @uirouter/core v5.0.4

I am using loading the js in the order of angular-ui-router , uirouter core and sticky-state.

@mevludin90
Copy link

mevludin90 commented Jul 5, 2017

@mohanrao I ran into this problem as well. As @christopherthielen mentioned it's because of ui-router-core is loaded twice. See this notice in angular-ui-router.js.

NOTICE: This monolithic bundle also bundles the @uirouter/core code.
 *         This causes it to be incompatible with plugins that depend on @uirouter/core.
 *         We recommend switching to the ui-router-core.js and ui-router-angularjs.js bundles instead.
 *         For more information, see http://ui-router.github.io/blog/angular-ui-router-umd-bundles

I know you released bower components for ui-router-core and ui-router-sticky states in this other issue. If you look closely at the files in bower_components/angular-ui-router/release, you'll notice ui-router-angular.js is included. So the last thing you need to do is add to bower.json's overrides section to change the main file loaded by angular-ui-router. This is where you would define the dependencies as well. For my purposes, I made it require both core and sticky-states.


"overrides": {
  "angular-ui-router": {
    "main": [
      "release/ui-router-angularjs.js"
    ],
    "dependencies": {
      "angular":">= 1.2.0",
      "ui-router-core": "*",
      "ui-router-sticky-states": "*"
    }
  }
}

@mohanrao
Copy link

mohanrao commented Jul 5, 2017

Thanks @mevludin90 . I forgot put the notes on this issue. I figured out the same. And did the same in my bower.json

@cesiya23
Copy link

cesiya23 commented Sep 8, 2017

My project ran into the same issue after I installed @uirouter/core manually by the command yarn add @uirouter/core --dev, which actually upgrade the version to 5.0.6.

But everything works ok on release @uirouter/core@5.0.3. So I degrade dependencies...

$ npm ls | grep router
├── @uirouter/dsr@1.0.2
├─┬ @uirouter/sticky-states@1.4.1
├─┬ angular-ui-router@1.0.3
│ └── @uirouter/core@5.0.3

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

No branches or pull requests

5 participants