GithubHelp home page GithubHelp logo

ui-router / sticky-states Goto Github PK

View Code? Open in Web Editor NEW
42.0 12.0 15.0 2.35 MB

Sticky states for UI-Router 1.0

License: MIT License

JavaScript 5.28% TypeScript 94.72%
sticky-states state-tree ui-router plugin

sticky-states's Introduction

Sticky States

Greenkeeper badge

Sticky States for UI-Router 1.0  Build Status

Overview and Use Case

Sticky States allows two or more trees of states to run concurrently along side each other.

The initial use case for this functionality is to implement "tabs" for application modules. Using Sticky States, a single app can implement one state tree for each tab, and have them operate at the same time, in parallel to each other. Each tab is implemented as its own UI-Router state tree. While one tab is active, the other tab is inactivated, but can be reactivated without losing any work in progress.

For the tabs use case, Sticky States work best along with Deep State Redirect

Sticky State Lifecycle

A Sticky State is the root of a UI-Router state tree which can continue running even after it is "exited". The sticky state (and its children) have a different lifecycle than normal states. The views of a Sticky State (and all activated substates) are retained until one of two things happen:

  • The parent of the sticky state is exited
  • The parent of the sticky state is directly activated

If a sibling of the sticky state (or a child of a sibling) is activated, the sticky state tree will "inactivate". A transition back to the inactivate state will reactivate it.

Using

Sticky states works requires ui-router-core 2.0.0 or above. Run npm ls to check the version of ui-router-core included with the UI-Router distribution for your framework

1) Add Plugin

ng1

In Angular 1, register a plugin by injecting $uiRouterProvider in a config() block.

import {StickyStatesPlugin} from "@uirouter/sticky-states";

angular.module('myapp', ['ui.router']).config(function($uiRouterProvider) {
  $uiRouterProvider.plugin(StickyStatesPlugin);
});

ng2

In Angular 2, register a plugin in your UIRouterConfig class

import {StickyStatesPlugin} from "@uirouter/sticky-states";

export class MyConfig {
  configure(uiRouter: UIRouter) {
    uiRouter.plugin(StickyStatesPlugin);
  }
}

react

In React, register a plugin after creating your UIRouterReact instance.

import {StickyStatesPlugin} from "@uirouter/sticky-states";

let router = new UIRouterReact();
router.plugin(StickyStatesPlugin);

Or, if using component bootstrapping, add the plugin in your routerConfig function.

let routerConfig = (router) => router.plugin(StickyStatesPlugin);

return <UIRouterReact config={routerConfig}/>;

2) Mark a state as sticky

The sticky state's view must target a named ui-view. The named ui-view must not be shared with other states.

Create and target a named ui-view.

  <ui-view name="admin"></ui-view>
let adminModule = {
  name: 'admin',
  sticky: true,
  views: {
    admin: { component: AdminComponent }
  }
}

The AdminComponent should remain active in the ui-view named admin, even if a sibling state is activated.

3) Show/Hide the sticky component

When a sticky state is inactive, it's often desired to hide the contents from the UI. This can be achieved using StateService.includes.

In some cases, ui-sref-active may also be used to toggle a class on the named ui-view.

Example

These minimal examples show how to get started with sticky states in:

More

This project was ported from the UI-Router Extras project for legacy UI-Router. For more information, see the ui-router extras documentation: http://christopherthielen.github.io/ui-router-extras/#/sticky

TODO: Rewrite docs

sticky-states's People

Contributors

christopherthielen avatar dependabot-preview[bot] avatar dependabot-support avatar dependabot[bot] avatar greenkeeper[bot] avatar greenkeeperio-bot avatar mgerstenblatt avatar uirouterbot avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sticky-states's Issues

Import required even when `exitSticky` property (on `TransitionOptions`) needs to be used.

When a statement, similar to the one below:

UIRouter.stateService.go(
          ..., //1st param - StateOrName
          ..., //2nd param - RawParams
          { exitSticky: this.constants.STATES.SHELL } // 3rd param - TransitionOptions
        );

is used, where exitSticky is needed as part of TransitionOptions, and nowhere in the code an import to @ui-router/sticky-states is required, an error is reported as follows:

Argument of type '{ exitSticky: string; }' is not assignable to parameter of type 'TransitionOptions'.

Current resolution to this:

Add import anywhere in one of the files as follow: import '@ui-router/sticky-states';
This is however ideally not required when using this library.

Same component in multiple views

Is it possible to render the same component (but a different instance, with different data and so on...) in a different view?

The use case is: I have a home state which is a sticky state. I have a modal view which is used to show a modal dialog that has a header, a footer and a main views. The header and the footer of the home and the modal are very very very similar (in fact the only thing that changes is the data that is rendered in the header).

If I try to render my header component in the modalheader ui-view, the header ui-view is cleared out.

So my question is: is there a way I can render the same component (but different instance) in two different views at the same time?

is the build stable ?

Sorry for the potentialy noob question, but is the build stable ?
Here is my trace :

C:\workspace\other\sticky-states>npm install
npm WARN deprecated [email protected]: Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue

> [email protected] install C:\workspace\other\sticky-states\node_modules\phantomjs-prebuilt
> node install.js

Considering PhantomJS found at C:\devtools\phantomjs\2.1.1\bin\phantomjs.EXE
Found PhantomJS at C:\devtools\phantomjs\2.1.1\bin\phantomjs.EXE ...verifying
Writing location.js file
PhantomJS is already installed on PATH at C:\devtools\phantomjs\2.1.1\bin\phantomjs.EXE
[email protected] C:\workspace\other\sticky-states
+-- @types/[email protected]
+-- @types/[email protected]

...

|   `-- [email protected]
`-- [email protected]

npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@^1.0.0 (node_modules\chokidar\node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for [email protected]: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})

C:\workspace\other\sticky-states>npm run build

> [email protected] build C:\workspace\other\sticky-states
> npm run clean && tsc && tsc -p tsconfig.esm.json


> [email protected] clean C:\workspace\other\sticky-states
> shx rm -rf lib lib-esm

src/stickyStates.ts(3,22): error TS2305: Module '"C:/workspace/other/sticky-states/node_modules/ui-router-core/lib/index"' has no exported member 'UIRouterPluginBase'.
src/stickyStates.ts(3,42): error TS2305: Module '"C:/workspace/other/sticky-states/node_modules/ui-router-core/lib/index"' has no exported member 'TransitionHookPhase'.
src/stickyStates.ts(3,63): error TS2305: Module '"C:/workspace/other/sticky-states/node_modules/ui-router-core/lib/index"' has no exported member 'TransitionHookScope'.
src/stickyStates.ts(3,84): error TS2305: Module '"C:/workspace/other/sticky-states/node_modules/ui-router-core/lib/index"' has no exported member 'TransitionServicePluginAPI'.
src/stickyStates.ts(4,68): error TS2305: Module '"C:/workspace/other/sticky-states/node_modules/ui-router-core/lib/index"' has no exported member 'PathType'.
src/stickyStates.ts(6,62): error TS2305: Module '"C:/workspace/other/sticky-states/node_modules/ui-router-core/lib/index"' has no exported member 'pushTo'.
src/stickyStates.ts(6,90): error TS2305: Module '"C:/workspace/other/sticky-states/node_modules/ui-router-core/lib/index"' has no exported member 'assertMap'.
src/stickyStates.ts(68,47): error TS2339: Property '_pluginapi' does not exist on type 'TransitionService'.
src/stickyStates.ts(82,35): error TS2339: Property 'onCreate' does not exist on type 'TransitionService'.
src/stickyStates.ts(120,29): error TS2339: Property '$$state' does not exist on type '{}'.
src/stickyStates.ts(123,60): error TS2345: Argument of type '{}[]' is not assignable to parameter of type 'PathNode[]'.
  Type '{}' is not assignable to type 'PathNode'.
    Property 'state' is missing in type '{}'.

Do you have a setup a location somewher to download a stable build ? I quickly googled it but couldn't find any.

Thanks for your help (and all this amazing work)

How to setup "sticky-states" with views

Hello,

At my work we have a large application using ng1 + ui-router. Our application consists of tens of widgets implemented into view of parent state/page. Each widget displays some charts/tables and requests data on start (those requests are slow because of lot of data). So we wanted to use sticky states to preserve widget state when users goes to another widget and then back. Unfortunately, it doesnt work as expected.

Here is a simple example, the problem is:

  1. when in sticky1 or sticky2 state, it is not possible to go to other sticky state (transitions returns "success" but its not working).
  2. when going from any sticky state to home and then back to sticky, the state of sticky is not preserved.

Even though, this looks like bugs to me, I guess those are not and this is just not intended way to use the sticky-states. Is there any way how to make this work?

Thank you

UI-router uiOnParamsChanged event is not triggered when StickyStatesPlugin is enabled

@christopherthielen,

I use uiOnParamsChanged event for catching dynamic state parameter change, but when I enable StickyStatesPlugin it stop propagation. Spend some time drilling down issue and definitely lives under "_calculateStickyTreeChanges" method as a part of "_addCreateHook" hook.

Seems like this line var shouldRewritePaths = ['entering', 'exiting'].some(function (path) { return !!simulatedTC[path].length; }); sais that it needs to overwrite path and in the same time reverts state params changes that supposed to trigger "uiOnParamsChanged" event in future.

Reproduced on:
"uirouter/core": "5.0.3",
"uirouter/angularjs": "1.0.3",
"uirouter/sticky-states": "1.4.1",

Please notice, this plugin will not work (application crashes, webpack build crashes) with upper version of "uirouter/core" or "uirouter/angularjs"

Thanks for your help!

Check to see if sticky state is loaded?

Maybe I am thinking about this incorrectly, but I am trying to find a way to find what states are loaded (I have one sticky state, and another dialog state on top of it). When I come in from the server, it won't load the sticky state automatically so I want to trigger a $state.go on it if it's not loaded. Thanks!

Stuck sticky states

Description

Navigating to a non sticky state (State B) from a sate (State A.1) whose parent is a sticky state (State A) and back (so you're in State A.1 again), then going to another state (State A.2) within the sticky state parent (State A) will result in the DOM of State A.1 to still be present.

Reproduce

Here's a repo demonstrating this behaviour: https://github.com/dcarabott/ui-router-sticky-states-stuck

In the repo the Shell state is the abstract sticky states having the Main and Full Page as a separate abstract child ui-views. In the Main state there are two other separate child ui-views; Lobby and Content. You can land on any of the Lobby child states; All and Others, the Content route, the Home route found in Full Page and on the Dialog route. ThDialog is the only route under Overlay and not Shell, where Overlay is not a sticky state. And that's where the error happens: going from a state within Shell to the Dialog and back.

Steps

  1. Land on any state within Shell (Lobby All, Lobby Others, Content, Home)
  2. Navigate to another Shell states and you'll notice that everything is working fine there
  3. Navigate to the Dialog found within Overlay. You'll notice that we can still see the Shell state from before since Shell is the sticky state
  4. Navigate back to a Shell state, ideally not the same one (So if you were in Home before Dialog, navigate to Content now for example)
  5. You'll notice that the state you navigated from to Dialog is still there. The DOM is still present and even the component instance. Example: Start by landing on Home. Navigate to Dialog. Navigate to Content. You can still see Home. So you end up with both Home and Content views.

Debugging

There are no error logs. We tried to dig in the issue and it looks like the sticky state (Shell in our case) is not being deactivated and reactivated

Let me know if you need more details or if I wasn't clear enough. Hopefully the POC will do a better job at explaining.

$stickyStateProvider

The $stickyStateProvider is missing and no apparent substitute is available on the $uiRouter object. Transitioning from one sticky state to another and then back to the parent tree won't reset the view. See $stickyState.reset in legacy documentation.

Bug with dynamic parameters

Consider the following (somewhat complicated) scenario:

{
	name: 'stateA'
},
{
	name: 'stateB',
	sticky: true,
	url: '/stateB?paramB',
	params: {
		paramB: {
			dynamic: true
		}
	}
}

Let's assume stateB is inactive and paramB is equal to 1. The current url is /stateB/?paramB=1. stateA is currently active. If at this moment we do $state.go('stateB', { paramB: 2 }); the url does not get updated to /stateB/?paramB=2, it stays at /stateB/?paramB=1.

Digging into it a bit, in @ui-router/sticky-states https://github.com/ui-router/sticky-states/blob/master/src/stickyStates.ts#L197:

let simulatedTC = PathUtils.treeChanges(inactiveFromPath, tc.to, trans.options().reloadState);

This calls into @ui-router/core, the method above returns https://github.com/ui-router/core/blob/master/src/path/pathFactory.ts#L110-L138, specifically:

 // Create a new retained path (with shallow copies of nodes) which have the params of the toPath mapped
let retainedWithToParams  = retained.map(applyToParams);
entering              = toPath.slice(keep);
to                    = (retainedWithToParams).concat(entering);

return { from, to, retained, exiting, entering };

This method returns the original retained, not retainedWithToParams.

Back to @ui-router/sticky-states this uses https://github.com/ui-router/sticky-states/blob/master/src/stickyStates.ts#L204:

tc.reactivating = simulatedTC.retained.slice(tc.retained.length);

So reactivating here ends up being the inactive state but with still the old parameters, not the new ones.

There are probably a few different ways to fix this, not sure what would be the preferred one.

Please let me know if this makes sense or whether there is any additional information I should submit.

Thank you.

Sticky states + "orphan" state + onExit = transition error

From @what-is-a-crow on January 12, 2018 20:15

We're attempting to upgrade ui-router to the latest supported version (from 0.2.18) and are having issues navigating from parent-less (in our case, fly-out / modal) states. Many of our application's forms are in such states and, on successful submission, we tend to do a $state.go("normal-state", {}, { reload: true }) to be sure that the user's changes are reflected in the previous state. If the "to" state has an onExit defined, however, we get the following ui-router exception: Transition Rejection($id: 0 type: 6, message: The transition errored, detail: TypeError: Cannot read property 'reduce' of undefined) with the underlying error happening at ResolveContext.getTokens.

I was able to create a Plunker example to reproduce: http://plnkr.co/edit/PdCjLhosY9LE9aOzLTfA

Our suspicion is src/path/pathUtils.ts:187 where an unsuccessful lookup returns undefined instead of an empty array but it could be something further up the stack.

Copied from original issue: ui-router/core#111

exitSticky typings issue

As per the code, can we update exitSticky to be an option parameter on TransitionOptions. I'm happy to do a PR for this if you agree.

Action required: Greenkeeper could not be activated 🚨

🚨 You need to enable Continuous Integration on all branches of this repository. 🚨

To enable Greenkeeper, you need to make sure that a commit status is reported on all branches. This is required by Greenkeeper because it uses your CI build statuses to figure out when to notify you about breaking changes.

Since we didn’t receive a CI status on the greenkeeper/initial branch, it’s possible that you don’t have CI set up yet. We recommend using Travis CI, but Greenkeeper will work with every other CI service as well.

If you have already set up a CI for this repository, you might need to check how it’s configured. Make sure it is set to run on all new branches. If you don’t want it to run on absolutely every branch, you can whitelist branches starting with greenkeeper/.

Once you have installed and configured CI on this repository correctly, you’ll need to re-trigger Greenkeeper’s initial pull request. To do this, please delete the greenkeeper/initial branch in this repository, and then remove and re-add this repository to the Greenkeeper App’s white list on Github. You'll find this list on your repo or organization’s settings page, under Installed GitHub Apps.

Sticky States on ng2

I've been trying to use sticky states with ng2. The 'Sticky States' feature looks like it requires ui-router-core >= 2.0.0. ng2 is currently on ui-router-core = 1.0.1. I could be missing something here, but does this mean that at this point in time its not possible to use 'Sticky States' with ng2 since it requires a more recent version of ui-router-core due to its 'plugin' functionality?

Support to use same view for multiple tabs when using with angularjs component

Hi,

I am trying to use the stick-states using the angularjs component.

If I use the ui-router to target a single named view with different components based on the url it is working.

But If I use the sticky states to target a single named view with different components based on the url it is not working.

I have to use the different named views to make it work. Like in the example of stick-states.

Is there a way I can achieve one named view to work with component based on the url along with the sticky states?

Sticky state resetting issue

USE CASE:
Consider having two sticky states with no parent (orphan).
a) Activate first state A (works fine and remains sticky)
b) Activate second state B
Action b) resets the state A resetting it's context, after which switching states works fine
@christopherthielen please let me know if I'm wrong
Any advice would be a great help, Thanks

Trouble using angularjs + npm + scripts solution

  1. Hit npm install @uirouter/core @uirouter/sticky-states @uirouter/angularjs.
  2. Copy ui-router-core[.min].js, ui-router-sticks-states[.min].js and ui-router-angularjs[.min].js to a directory where you can load it from, e.g. your dist directory.
  3. Include the scripts in your index.html:
<script src="/deps/angular-ui-router/release/ui-router-core.js"></script>
<script src="/deps/angular-ui-router/release/ui-router-sticky-states.js"></script>
<script src="/deps/angular-ui-router/release/ui-router-angularjs.js"></script>
  1. Add the following code to your app.module.js (or similar):
angular.module('app.state', ['ui.router']).config([
  '$uiRouterProvider',
  function($uiRouterProvider) {
    var StickyStates = window['@uirouter/sticky-states'];
    $uiRouterProvider.plugin(StickyStates.StickyStatesPlugin);
  }
]);

Originally posted by @molehillrocker in #4 (comment)

I tried this solution. It somehow did not work for me. Sticky states did not work as expected. I then printed out the variable StickyStates which prints an empty object "{ }" and when I print out StickyStates.StickyStatesPlugin, it tells me that it is undefined!

Where am I making a mistake?

Here is the stub from my index.html:

` <script src="scripts/plugins/angular-ui-router/release/ui-router-core.min.js" ></script>

<script src="scripts/plugins/angular-ui-router/release/ui-router-sticky-states.js" ></script> <script src="scripts/plugins/angular-ui-router/release/ui-router-dsr.min.js" ></script> <script src="scripts/plugins/angular-ui-router/release/ui-router-angularjs.js" ></script> <script src="scripts/plugins/angular-ui-router/release/angular-ui-router.min.js" ></script> <script src="scripts/plugins/angular-ui-router/release/resolveService.min.js" ></script> <script src="scripts/plugins/angular-ui-router/release/stateEvents.min.js" ></script>

`

And, here is my app.js:

.config(['$uiRouterProvider', function($uiRouterProvider) { var StickyStates = window['@uirouter/sticky-states']; console.log("StickyStates: " + JSON.stringify(StickyStates)); console.log("StickyStatesPlugin: " + JSON.stringify(StickyStates.StickyStatesPlugin)); $uiRouterProvider.plugin(StickyStatesPlugin); }]);

Please help.

_calculateStickyTreeChanges() loses new params

When using sticky states, reloading the same state with new params doesn't work as expected.
I tracked it to _calculateStickyTreeChanges().

 // Rewrite the to path
  tc.to = tc.retained.concat(tc.reactivating).concat(tc.entering);

tc.retained has the old params so overwriting tc.to causes the transition to be rejected with "The transition was ignored" message.

Not sure what the best fix is.

Action required: Greenkeeper could not be activated 🚨

🚨 You need to enable Continuous Integration on all branches of this repository. 🚨

To enable Greenkeeper, you need to make sure that a commit status is reported on all branches. This is required by Greenkeeper because it uses your CI build statuses to figure out when to notify you about breaking changes.

Since we didn’t receive a CI status on the greenkeeper/initial branch, it’s possible that you don’t have CI set up yet. We recommend using Travis CI, but Greenkeeper will work with every other CI service as well.

If you have already set up a CI for this repository, you might need to check how it’s configured. Make sure it is set to run on all new branches. If you don’t want it to run on absolutely every branch, you can whitelist branches starting with greenkeeper/.

Once you have installed and configured CI on this repository correctly, you’ll need to re-trigger Greenkeeper’s initial pull request. To do this, please delete the greenkeeper/initial branch in this repository, and then remove and re-add this repository to the Greenkeeper App’s white list on Github. You'll find this list on your repo or organization’s settings page, under Installed GitHub Apps.

OnRetain not called

When sticky state with dinamic=true param is deactivated (after previously activated) and activated again with different parameter value, OnRetain is not called. If state is not sticky, everything work as predicted - onRetain is called.

ng -v output:

Angular CLI: 1.7.3
Node: 10.0.0
OS: linux x64
Angular: 5.2.10
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, router

@angular/cdk: 5.2.4
@angular/cli: 1.7.3
@angular/flex-layout: 5.0.0-beta.14
@angular/material: 5.2.4
@angular-devkit/build-optimizer: 0.3.2
@angular-devkit/core: 0.3.2
@angular-devkit/schematics: 0.3.2
@ngtools/json-schema: 1.2.0
@ngtools/webpack: 1.10.2
@schematics/angular: 0.3.2
@schematics/package-update: 0.3.2
typescript: 2.7.2
webpack-bundle-analyzer: 2.11.1
webpack: 3.11.0

ui-router modules:

@uirouter/angular: "1.0.1",
@uirouter/sticky-states: "^1.5.0",


Exit/Reset a Sticky State

Having an issue when exiting a sticky state. Currently the API offers two options to exit a Sticky State (correct me if I'm wrong).

First Option:

Setting an exitState property when redirecting to another state.

Example:
this.router.stateService.go('app.new-state', {}, {exitSticky: 'app.sticky-state'})

Second Option

Using this.router.getPlugin('stickystates').exitSticky() to exit all inactive sticky states. This option enables you to exit a specific state as well.

If that's all correct, this means that to exit a sticky state it has to first be inactive, which makes sense. What this also means is that in order to exit a sticky state you have to redirect to another state even if that sticky state is already inactive. This case creates a problem.

Test Scenario
Let's say we have 2 states: app.normal-state and app.sticky-state, which as the name implies the app.sticky-state is a sticky: true state.

The user is currently on app.sticky-state and there's a close button which should take him to app.normal-state. This scenario requires that after closing, the inactive sticky state should be exited.

If the first option is used (this.router.stateService.go('app.normal-state', {}, {exitSticky: 'app.sticky-state'})) it won't work since app.sticky-state is not inactive.

If the second option is used (this.router.getPlugin('stickystates').exitSticky()) by having some timeout after the state successfully transitions, the following API code will trigger:

  exitSticky(states?: any) {
    let $state = this.router.stateService;
    if (states === undefined) states = this._inactives.map(node => node.state.name);
    if (isString(states)) states = [states];

    return $state.go($state.current, {}, {
      inherit: true,
      exitSticky: states
    });
  }

This code will not work as the $state.go will redirect to the current state which will not have any effect. A reload: true would trigger an effect.

The problem here is that in order to exit a sticky state, one has to transition to another state. This is not always the case. It would be best to have an option which just exits inactive sticky states without transitioning to a new state. On the previous UIRouterExtras project this was possible using this.$stickyState.reset('*').

Not able to navigate back

After applying sticky to the view not able to navigate back.

After reaching to sticky state when trying to ui-sref as well as $state.go , not able to navigate back.
Parent is not sticky and child is sticky. I am not able to navigate from child to parent.

@christopherthielen can you please help me on this, you help really appreciated.

How to deactivate existing sticky state

USE-CASE
Lets suppose if i navigate from Non-Sticky to Sticky, and i want to give a provision to user if user wants to reset sticky view and go back.
But second time if user come back to that state again it will activate the sticky for that view.

I think we can't do reload for this, because we have go sticky to non-sticky same time.
Even we can't reset by passing params. beacuse if we do by using params then it will persist same view for different params . @christopherthielen please corrent if im wrong.

Any help would be useful. Thanks

How to Install on Ng1

Hello,

can you please provide more information about how to install the sticky-states for Angular 1. I'm using bower instead of NPM. How about the ui-router-core on bower?

How should I import the required files? Usually I'm using the <script> tag to import files.
The Import-Function doesn't work on the Client-Side (or require).

import {StickyStatesPlugin} from "ui-router-sticky-states";

angular.module('myapp', ['ui.router']).config(function($uiRouterProvider) {
$uiRouterProvider.plugin(StickyStatesPlugin);
});

Best Regards

Resolves not working when enabling the sticky states plugin

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

toggle show sub-views like TAB behavior

I made a react app with ui-router and sticky-state . I just want switch show views between sub-views of Home (you may notice i do sth in Home component . but its not working by some weird issue) .

How to straightforwardly approach my target ? thank !

//route
const configRouter = router => {
    // default to home
    router.urlRouter.otherwise('/home');

    //transition watch
    router.transitionService.onEnter({}, (trans, state) => {
        // ...
        console.log(trans, state);
    });
};

class App extends Component {
    render() {
        return (
            <UIRouter plugins={[hashLocationPlugin, StickyStatesPlugin]}
                      states={states}
                      config={configRouter}>
                <div>
                    <UIView name="home" />
                    <Footer />
                </div>
            </UIRouter>
        );
    };
}

//Home 
class Home extends Component {
    constructor(props) {
        super(props);
    }

    state = ({
        num: 2
    });

    render() {
        const {stateService} = this.props.transition.router;

        let style1 = {display: stateService.includes('home.tab1') ? 'block' : 'none'},
            style2 = {display: stateService.includes('home.tab2') ? 'block' : 'none'},
            style3 = {display: stateService.includes('home.tab3') ? 'block' : 'none'},
            style4 = {display: stateService.includes('home.tab4') ? 'block' : 'none'},
            style5 = {display: stateService.includes('home.tab5') ? 'block' : 'none'};      

        return (<div className="home">
            <Header />
            <div className="scroll-content scroll-content-top scroll-content-bottom">
                <ul className="tabHeader row">
                    {[1, 2, 3, 4, 5].map((val, index) => (
                        <UISrefActive class="active" key={index}>
                            <UISref to={'.tab' + val.toString()}>
                                <a key={index}
                                   className="col-fifth text-center height100p">
                                    {val.toString()}
                                </a>
                            </UISref>
                        </UISrefActive>
                    ))}
                </ul>
                <div>
                    <div className="homeTab1" style={style1}><UIView name="homeTab1" /></div>
                    <div className="homeTab2" style={style2}><UIView name="homeTab2" /></div>
                    <div className="homeTab3" style={style3}><UIView name="homeTab3" /></div>
                    <div className="homeTab4" style={style4}><UIView name="homeTab4" /></div>
                    <div className="homeTab5" style={style5}><UIView name="homeTab5" /></div>
                </div>
            </div>
        </div>);
    };
}

//homeStates
const homeState = [
    {
        name: 'home',
        url: '/home',
        sticky: true,
        redirectTo: 'home.tab1',
        views: {
            'home': {
                component: Home
            }
        }
    },
    {
        name: 'home.tab1',
        url: '/1',
        sticky: true,
        views: {
            'homeTab1': {
                component: HomeTab1
            }
        }
    },
    {
        name: 'home.tab2',
        url: '/2',
        sticky: true,
        views: {
            'homeTab2': {
                component: () => (<div>HomeTab2</div>)
            }
        }
    },
    {
        name: 'home.tab3',
        url: '/3',
        sticky: true,
        views: {
            'homeTab3': {
                component: () => (<div>HomeTab3</div>)
            }
        }
    },
    {
        name: 'home.tab4',
        url: '/4',
        sticky: true,
        views: {
            'homeTab4': {
                component: () => (<div>HomeTab4</div>)
            }
        }
    },
    {
        name: 'home.tab5',
        url: '/5',
        sticky: true,
        views: {
            'homeTab5': {
                component: () => (<div>HomeTab5</div>)
            }
        }
    }
];

Error with sticky-states and chromium 40

Since the most recent updates I've encountered some strange behaviour when using sticky-states with angular 1.6.8 in chromium 40 (on OS X and also a lite linux embedded distribution). I had it working before but now I just can't find a combination of ui-router and sticky-states to make it work again. Works fine in chrome 63 and multiple firefox versions (on OS X).

Have tried bundling the setup using browserify and right now using webpack, but I get the same results on startup:

Rejection {
  $id: 0, 
  type: 6, 
  message: "The transition errored", 
  detail: TypeError: undefined is not a function
}

I have narrowed it down to sticky-states since it works fine if I don't add it as a plugin.

package.json

"dependencies": {
    "@uirouter/angularjs": "^1.0.14",
    "@uirouter/sticky-states": "^1.5.0",
    "angular": "^1.6.8",
    ...
}

output of "npm ls | grep router"

├─┬ @uirouter/[email protected]
│ └── @uirouter/[email protected]
├── @uirouter/[email protected]
npm ERR! extraneous: [email protected] 

sticky-states usage

var angular = require('angular');

angular
    .module('esd4')
    .config(function ($uiRouterProvider) {
        var StickyStatesPlugin = require('@uirouter/sticky-states').StickyStatesPlugin;
        // Works fine if I comment out the following:
        $uiRouterProvider.plugin(StickyStatesPlugin);
    });

webpack.config.json

/* global __dirname */

'use strict';

// Plugins
var HtmlWebpackPlugin = require('html-webpack-plugin');
var VisualizerPlugin = require('webpack-visualizer-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var CleanWebpackPlugin = require('clean-webpack-plugin');

var path = require('path');
var _defaults = require('lodash/defaults');

module.exports = function (options) {
    options = _defaults(options, {
        target: 'dev',
    });

    var config = {
        entry: './app/app.js',
        output: {
            filename: 'bundle.js',
            path: path.resolve(__dirname, 'dist'),
        },
        devServer: {
            overlay: true,
            contentBase: path.resolve(__dirname, 'dist'),
            proxy: {
                // proxy all calls to /api/* to the mock api
                '/api': {
                    target: 'http://localhost:9090',
                },
            },
            host: 'localhost',
            port: 8080,
        },
        plugins: [
            new CleanWebpackPlugin('dist', {
                // Only remove dist when building for production
                dry: options.target === 'dev',
            }),
            new HtmlWebpackPlugin({
                template: './app/index.html',
                minify: {
                    removeComments: true,
                    collapseWhitespace: true,
                    removeAttributeQuotes: true,
                    useShortDoctype: true,
                    keepClosingSlash: true,
                    removeScriptTypeAttributes: true,
                },
            }),
            new VisualizerPlugin({
                filename: '../reports/webpack-visualize-statistics.html',
            }),
            new ExtractTextPlugin({
                filename: '[name]-[hash].css',
            }),
        ],
        module: {
            rules: [
                {
                    enforce: 'pre',
                    test: /\.scss$/,
                    loader: 'import-glob-loader',
                },
                {
                    test: /\.scss$/,
                    use: ExtractTextPlugin.extract({
                        fallback: 'style-loader',
                        use: [
                            {
                                loader: 'css-loader',
                                options: {
                                    importLoaders: 1,
                                },
                            },
                            {
                                loader: 'sass-loader',
                            },
                        ],
                    }),
                },
                {
                    test: /\.js$/,
                    exclude: /(node_modules)/,
                    use: [
                        {
                            loader: 'ng-annotate-loader',
                            options: {
                                add: true,
                                map: false,
                            },
                        },
                        {
                            loader: 'eslint-loader',
                            options: {
                                quiet: true,
                            },
                        },
                    ],
                },
                {
                    test: [/\.html$/],
                    use: [
                        {
                            loader: 'html-loader',
                        },
                    ],
                },
                {
                    test: /\.(woff)$/,
                    use: [
                        {
                            loader: 'file-loader',
                        },
                    ],
                },
            ],
        },
    };

    return config;
};

Any clues as to what might happen? I'm really struggling here and can't think of a reasonable solution.

Modal Infinity

Hello,

I understand that sticky-states doesn't necessarily (officially) support Modals (in this case Bootstrap). However I've had success wiring up modals shown below.

The problem I'm running into is that modals could have ui-srefs to other potential modals. Now, currently the opening of new modals works fine, but upon closing, the modal behind the one that was closed does not activate nor does it even function at all. For example, many modals have tabs in them, which allow it to switch to intermediate sub-states. Clicking those tabs does not do anything at all.

I'm guessing there is something happening in the pushing and popping of states that sticky-states wraps to enable it's magic, but I'm having trouble digging in and finding the exact location.

modalStateProvider

myApp.provider '$modalState', ($stateProvider, $uibModalProvider) ->
  provider = this

  @$get = ->
    provider

  @state = (stateName, options) =>
    modalInstance = undefined
    currentState = undefined

    $stateProvider.state stateName,
      parent: options.parent
      url: options.url
      params: options.params
      redirectTo: options.redirectTo
      sticky: true # <--------- Needed

      onEnter: ($uibModal, $state, $rootScope, $window) ->

        currentState  = $state.$current
        modalScope    = $rootScope.$new();

        modalScope.cancel = () ->
          modalInstance.close()

        modalInstance = $uibModal.open(
          templateUrl: options.templateUrl
          controller: options.controller
          size: 'lg'
          windowClass: 'modal modal-slide-in-right'
          scope: modalScope
        )

        modalInstance.result['finally'] ->
          modalInstance = null
          # return $state.go(currentState)
          return
        return
      onExit: ->
        if modalInstance
          modalInstance.close()
        return

    return provider

  return

You can then use it like so...

    $modalStateProvider.state 'users',
      parent: 'modal'
      url: '/users'
      templateUrl: 'users/index.html'
      controller: 'UsersController'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.