Coder Social home page Coder Social logo
core

ui-router / core Goto Github PK

View Code? Open in 1sVSCode Editor NEW
118.0 10.0 54.0 8.67 MB

UI-Router Core: Framework agnostic, State-based routing for JavaScript Single Page Apps

License: MIT License

JavaScript 0.42% TypeScript 99.58%
router ui-router state-machine state-tree javascript typescript framework-agnostic transitions

core's Introduction

UI-Router Core  Build Status

UI-Router core provides client-side Single Page Application routing for JavaScript. This core is framework agnostic. It is used to build UI-Router for Angular 1, UI-Router for Angular 2, and UI-Router React.

SPA Routing

Routing frameworks for SPAs update the browser's URL as the user navigates through the app. Conversely, this allows changes to the browser's URL to drive navigation through the app, thus allowing the user to create a bookmark to a location deep within the SPA.

UI-Router applications are modeled as a hierarchical tree of states. UI-Router provides a state machine to manage the transitions between those application states in a transaction-like manner.

Features

UI-Router Core provides the following features:

  • State-machine based routing
    • Hierarchical states
    • Enter/Exit hooks
  • Name based hierarchical state addressing
    • Absolute, e.g., admin.users
    • Relative, e.g., .users
  • Flexible Views
    • Nested Views
    • Multiple Named Views
  • Flexible URLs and parameters
    • Path, Query, and non-URL parameters
    • Typed parameters
      • Built in: int, string, date, json
      • Custom: define your own encoding/decoding
    • Optional or required parameters
    • Default parameter values (optionally squashed from URL)
  • Transaction-like state transitions
    • Transition Lifecycle Hooks
    • First class async support

Get Started

Get started using one of the existing UI-Router projects:

Build your own

UI-Router core can be used implement a router for any web-based component framework. There are four basic things to build for a specific component framework:

UIView

A UIView is a component which acts as a viewport for another component, defined by a state. When the state is activated, the UIView should render the state's component.

UISref (optional, but useful)

A UISref is a link (absolute, or relative) which activates a specific state and/or parameters. When the UISref is clicked, it should initiate a transition to the linked state.

UISrefActive (optional)

When combined with a UISref, a UISrefActive toggles a CSS class on/off when its UISref is active/inactive.

Integrate with your framework's bootstrap mechanism (optional)

Implement framework specific bootstrap requirements, if any. For example, UI-Router for Angular 1 and Angular 2 integrates with the ng1/ng2 Dependency Injection lifecycles. On the other hand, UI-Router for React uses a simple JavaScript based bootstrap, i.e., new UIRouterReact().start();.

Minimal Example

This example doesn't have UIView, UISref, or anything like that. It bootstrap the router and naively manipulates the DOM when states are activated.

https://stackblitz.com/edit/ui-router-plain-javascript?file=index.js

Note: do not model your own router off this example, it is meant to show only the bare minimum.

Getting help

Create an issue or contact us on Gitter.

core's People

Contributors

christopherthielen avatar dependabot-preview[bot] avatar nateabele avatar dependabot[bot] avatar ksperling avatar timkindberg avatar wesleycho avatar PascalPrecht avatar jeme avatar greenkeeper[bot] avatar greenkeeperio-bot avatar gigadude avatar ysbaddaden avatar adambabik avatar amcdnl avatar fpipita avatar wawyed avatar ProLoser avatar csvn avatar eddiemonge avatar sbezkostnyi avatar cvn avatar asperry152 avatar bbodenmiller avatar chrislambe avatar davidpfahler avatar eduardomb avatar filipesilva avatar JakobJingleheimer avatar JanDalF avatar

Stargazers

 avatar maciek012 avatar Geoff Goodman avatar Jesus Franco avatar Chris Vouga avatar Alex Simonides avatar Ivan Chalyk avatar  avatar Kolja Kirchner avatar  avatar Quang Van avatar nour aldeen avatar rhlin avatar Lhar Gil avatar Sam Uherek avatar Neeraj avatar  avatar Maziar Barzi avatar Lucas Lunelli avatar  avatar x0rzkov avatar Dmitry Ryumkin avatar danielsdesk avatar Kostia P avatar Mathias Gheno Azzolini avatar Mihard avatar Kevin Nolan avatar Mario Sánchez García avatar Shir Bar Lev avatar M. Hamza Rajput avatar Tran Thanh Danh avatar Chris avatar Helgi Helgason avatar attl8d avatar John Simerlink avatar tiny avatar Chandler avatar Dee Cheung avatar lvxiaoxin avatar  avatar  avatar Michal avatar laden avatar chetna avatar Miguel Borges avatar Ian Lim avatar Casper Søkol avatar Jon Repp avatar Anton Evzhakov avatar Andrey Poznyak avatar Jun Liu avatar Benedict Khoo avatar  avatar Carlos Galarza avatar Kamil Maraz avatar Brian Abbott avatar Auban le Grelle avatar  avatar Chaudhry Junaid Anwar avatar Glauber Mota avatar Alexander Moon avatar Juan Antelo-Justiniano avatar Alexei Zaviruha avatar Will Murray avatar Wiley Marques avatar Romulo Cintra avatar senli avatar Alexander Malitsky avatar  avatar Gustavo Massaneiro avatar Anbad avatar Bruno Vollino avatar Maor Frankel avatar Chris Thielen avatar  avatar John Smith avatar Christopher Cortes avatar zhaojiami avatar  avatar Alexis Georges avatar nishitani yuki avatar Naoki OKAMURA avatar Yoriki Yamaguchi avatar azu avatar Gudjon avatar  avatar Ken Studdy avatar Fernando Gómez avatar Brian Mikinski avatar Jody Smith avatar Matt Newnham-Wheatley avatar Pankaj Parkar avatar Pavel Vostrikov avatar Haidar Diab avatar Aeo avatar Mohammad Rahhal avatar Alexander Strochkov avatar Bob Manary avatar yangyachao avatar Jordan Cole Hunt avatar

Watchers

Nate Abele avatar James Cloos avatar Wesley Cho avatar Chris Thielen avatar Minghao Ni avatar  avatar Marco Botto avatar geekchow avatar  avatar  avatar

core's Issues

Proposal: onBefore hooks should not be handled synchronously

We currently allow onBefore hooks to either synchronously return a HookResult a transition, or to return a promise. This was initially created to implement the synchronous $stateChangeStart polyfill.

However, having onBefore be handled differently than the other hooks has caused additional edge cases, and has been at the root of a number of reported bugs. It also increases the code size and complexity. My gut feel is that removing synchronous handling code will make transition hooks easier to reason about.

I propose that onBefore is handled as a normal async hook, very similar to onStart hooks. It seems most likely that any end users who currently use onBefore will be unaware of the change in implementation.

Synchronous redirects from `onBefore` can cause infinite redirect loop

88052bf added 20+ redirect loop detection, but it only was checked

  • when the redirected transition was run
  • after the sync hooks were processed

This meant a synchronous hook (onBefore) which returned a redirect could avoid the loop detection.

Moving this logic to the Transition.redirect() function should detect the 20+ loops at a more appropriate time.

uiCanExit also called for redirected states

[Using UI-Router 1.0]

I have a parent state with several child states to handle a tabbed view. The parent state redirects to the first child state using a redirectTo property.

In an unrelated state I have a component with a uiCanExit function in the controller that shows a confirmation dialog when there is unsaved data (with cancel, discard and save). When I press discard I resolve the dialog promise to true but the confirmation dialog is displayed again. To prevent this I need to make sure that the conditions for displaying the dialog are not met.

Is this a bug or intended feature?

Consider allowing FQN addressing for states w/ `parent:`

angular-ui/ui-router#3119
ui-router/react#11
angular-ui/ui-router#3181

If the state is defined using parent: notation, you cannot reference it by including the parents name. For example if a state is declared like this:

{
  parent: 'home',
  name: 'foo'
}

And you cannot create a sref such as ui-sref='home.foo' because home is not technically part of the name of the state.

This also affects glob patterns in transition hooks. Is not currently possible to match foo using this criteria object:

{ to: 'home.**' }

Implement `Resolve<T>` style interface for simpler DI

It would be nice to have an interface similar to Resolve<T> from @angular/router for specifying resolvables and their dependencies. This is especially true now given that the AoT compiler complains on non-exported anonymous resolveFn.

Current Behavior

export const personState = {
  name: 'person',
  url: '/people/:personId',
  component: Person,
  resolve: [
    { 
      token: 'person', 
      deps: [Transition, PeopleService],
      resolveFn: (trans, peopleSvc) => peopleSvc.getPerson(trans.params().personId)
    }
  ]
};

Proposed Interface

interface Resolve<T> {
  policy?: ResolvePolicy;
  resolve(transition: Transition): Observable<T>|Promise<T>|T;
}

Example Usage

@Injectable()
export class PersonResolve implements Resolve<Person> {
  constructor(private peopleSvc: PeopleService) {}

  resolve(transition: Transition) {
    return this.peopleSvc.getPerson(transition.params().personId);
  }
}

export const personState = {
  name: 'person',
  url: '/people/:personId',
  component: Person,
  resolve: {
    person: PersonResolve
  }
};

This would also enable exposing a more strict StateDeclaration interface using Mapped Types, something like:

interface ComponentStateDeclaration<T> { // T extends Component
  component: T;
  resolve?: { [P in keyof T]?: Resolve<T[P]>; }
}

Allowing users to (optionally, using the stricter interface) validate that their resolve tokens match the tokens and their types of the component definition at compile time.

One open question is whether it makes more sense to have users specify sibling/parent resolve dependencies in the constructor or the resolve function.

consider alternate API for lazyLoad

Not everyone wants to lazy load entire state definitions or trees of states.

Consider a lazy load API that allows anything such as components to be lazy loaded, without lazy loading a state definition.

The primary concern is that the current API expects the lazy load function to replace the current state. Essentially, if they state has a lazy load property, it is considered a future state and will be removed when it is loaded lazily and replaced by the new definition.

An alternate possibility is to keep a lazy load as is but do not use that as the criteria for State replacement. Instead, perhaps the states name suffix of .** could indicate that the state is a future state, for example: mail.** indicates that this state will be replaced by a new definition after lazy load is complete.

Allow view targeting to specify view on the current state

Named views currently target the parent state

views: {
  "foo": SomeComponent
}

The above is sugar for targeting foo at the parent state, i.e.:

views: {
  "[email protected]^": SomeComponent
}

We can also relatively target ui-views in the ancestors by adding on additional carets ^.^

views: {
  "[email protected]^.^": SomeComponent
}

Layout states

Consider a LayoutComponent that renders like:

<div id="layout">
  <ui-view name="header"></ui-view>
  <ui-view name="body"></ui-view>
  <ui-view name="footer"></ui-view>
</div>

Sometimes it may be useful to define a layout in a state, and also fill in the layout in that same state. However, a config like this will target the [email protected]^ (at the parent state):

views: {
  $default: LayoutComponent,
  header: HeaderComponent,
}

Of course, you could target the ui-view using fully qualified state name

views: {
  $default: LayoutComponent,
  "[email protected]": HeaderComponent,
}

But it might be nice to have a shorthand for "target a view on the current state":

views: {
  $default: LayoutComponent,
  "[email protected]": HeaderComponent,
  "[email protected]": BodyComponent,
  "[email protected]": FooterComponent,
}

strictNullChecks causing compiler error

Property 'from' of type 'string | boolean | Predicate<StateDeclaration> | undefined' is not assignable to string index type 'string | boolean | Predicate<StateDeclaration>'.

I get this error when enabling strictNullChecks. I've worked around it with the skipLibCheck option in my .tsconfig.json. I see some work was done to better support strictNullChecks, but I am still having an issue.

Thank you for your time.

Match Future states

When tying to access to /account which is lazy path it always redirect me to the static_page

export const app_states = [
    { name: 'account.**', url: '/account', lazyLoad: ... },
    { name: 'static_page', url: '/{name}', component: StaticPage },
]

to solve this the future states should be registered with the same order of it's parent or changing the matching rule

Changing URL

i have login folder and having login route Provider.

$routeProvider.when('/admdashboard', {
templateUrl : file_path+'/login/login/user',
controller : 'LoginCtrl'
});
app.controller('loginCtrl',function($location,$scope,$location,$http){
$http({
method:'POST',
url:file_path+'/api/v1/user/login,
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
data: $.param($scope.text)
}).success(function (data){
if(data.response != 'error'){
$location.path('/admdashboard');
$scope.$apply();
}
});

});

I can login successfully.but i am not able to direct?

After login successful how to redirect to administrator route provider without reloading the page

$routeProvider.when('/admdashboard', {
templateUrl : file_path+'/admin/dashboard/dashboard_tiles',
controller : 'AdmDashboardController'
});

Do not log TransitionRejection when the transition is ABORTED.

if (error.type === RejectType.ABORTED) {
router.urlRouter.update();
// Fall through to default error handler
}
}
let errorHandler = this.defaultErrorHandler();
errorHandler(error);
    const rejectedTransitionHandler = (transition: Transition) => (error: any): Promise<any> => {
      if (error instanceof Rejection) {
        if (error.type === RejectType.IGNORED) {
          // Consider ignored `Transition.run()` as a successful `transitionTo`
          router.urlRouter.update();
          return services.$q.when(globals.current);
        }

        const detail: any = error.detail;
        if (error.type === RejectType.SUPERSEDED && error.redirected && detail instanceof TargetState) {
          // If `Transition.run()` was redirected, allow the `transitionTo()` promise to resolve successfully
          // by returning the promise for the new (redirect) `Transition.run()`.
          let redirect: Transition = transition.redirect(detail);
          return redirect.run().catch(rejectedTransitionHandler(redirect));
        }

        if (error.type === RejectType.ABORTED) {
          router.urlRouter.update();
          // Fall through to default error handler
        }
      }

      let errorHandler = this.defaultErrorHandler();
      errorHandler(error);

      return services.$q.reject(error);
    };

Current behavior is to log ABORTED rejections. Enough people have requested not to log, or been confused about how to mute them. We should change so we no longer fall through to the default error handler.

Resolve names must start with a letter

[Foreword: I'm quite lost with the way the repos are organized so my apologies if this is not the right place to post this issue; please redirect me if need be]

Using Angular 1.6.1 and U-IRouter 1.0.0-beta3.

When I was mastering UI-Router legacy, I've made a habit of prefixing my resolves with an underscore character just to make them stand apart in my code. This does not work anymore with UI-Router 1.x.

Try this:

let appState = {
    name: 'app',
    url: '/',
    component: 'app',
    resolve: {
        _currentUser: [ 'auth.service', function(Auth)
        {
            return Auth.getCurrentUser()
        }]
    }
}
$stateProvider.state(appState)

/* ... */

angular.module('myapp').component('app', {
    bindings: {
        _currentUser: '<'
    },
    template: '<pre>{{ $ctrl._currentUser }}</pre>'
})

the <pre> is empty.

Now just rename _currentUser to currentUser (without the underscore) in both the state and component definitions, and it works.

Non-root base causing infinite redirect

I am trying to host a UI-Router project with a non-root path in the <base> tag:
https://wallzero.gitlab.io/ui-router-react-digest/
(I suggest observing with Chrome since it will handle the Uncaught RangeError: Maximum call stack size exceeded without crashing)

UI-Router is then configured to redirect to TabA state. However, with a non-root base, an additional / preceeds the route /taba resulting in an infinite redirect between /taba and //taba. When it finally stops, the url is https://wallzero.gitlab.io/ui-router-react-digest/

I've tried both <base href="/ui-router-react-digest/"> and <base href="/ui-router-react-digest">; with and without the trailing slash. It appears the urlRouter.otherwise function is prepending a /. I've tried the following which all produce the same result:

router.urlRouter.otherwise('/taba');
router.urlRouter.otherwise('taba');
router.urlRouter.otherwise(() => {
  return '/taba';
});
router.urlRouter.otherwise(() => {
  return 'taba';
});

Also, a root <base href="/"> works just fine.

You can run my project yourself: https://gitlab.com/wallzero/ui-router-react-digest/tree/master. Run via npm install && npm run build:demo:gitlab && npm run start:demo:dev:gitlab.

Working with `Transition.params` is unwieldy

This issue is just for improving Typescript experience. There is no functional changes intended.

When working with Typescript, using Transition.params() usually feels a bit clunky. A piece of code to illustrate the issue:

let toParams = transition.params();

console.log(toParams.id); // Warning; "prop id does not exist on type.."
console.log(toParams['id']); // ugly workaround

Both of the options above leaves the user with no intellisense in their editor. There is solution to the above, which is to cast params to interface/type, but this is messier (especially in one-liners):

let toParams = transition.params() as MyStateParams;

console.log(toParams.id); // yay!
console.log((transition.params() as MyStateParams).id); // not very pretty...

Ideally, I would like to be able to do this:

let toParams = transition.params<MyStateParams>();

console.log(toParams.id);
console.log(transition.params<MyStateParams>().id); // feels better than casting

Transitions which are aborted by an onBefore hook should not supersede other pending transitions

Currently a transition will always supersede any transitions that started before it - unfortunately, this includes transitions which never actually started themselves, due to being interrupted by an onBefore hook. For example, I wrote the following simple onBefore hook to prevent several clicks on the same ui-sref from "stacking up":

    mod.config(function($transitionsProvider) {
        let prevTarget = undefined;
        $transitionsProvider.onBefore({}, function(trans) {
            const target = trans.targetState().toString();
            if (prevTarget && target === prevTarget) return false;
            prevTarget = target;
        }, {priority: 9999});
    });

If you click the ui-sref several times, only the first transition will attempt to fetch its resolve data (as desired), with all later transitions immediately being aborted. However, once all resolve data have been retrieved, the first transition is also cancelled since other transitions were attempted.

Since onBefore is meant to be invoked before the transition even begins, it should be able to intercept and throw away unwanted transitions entirely, without having them interrupt other operations.

child states missing when lazy loading

From @spyro92 on March 17, 2017 1:9

This is a:

  • Bug Report
  • Feature Request
  • General Query

My version of UI-Router is: 1.0.0-rc.1

Bug Report

Current Behavior:

When lazy loading states, any states registered in modules that are dependencies to the module being lazy loaded will not be registered.

in the example: lazy.foo is being registered, but not lazy.bar

it seems to be a timing issue, if you remove the dependency on lazy.bar from the module 'lazy' the state will be registered.

Expected Behavior:

lazy.bar and lazy.foo should be registered

Link to Plunker that reproduces the issue:

http://plnkr.co/edit/IfxBMmsZgKVAyxSZ7w0g?p=info

Copied from original issue: angular-ui/ui-router#3375

<base> Tag Breaks Path Resolve

With @uirouter/[email protected] I've been able to upgrade to the latest. I fixed the Infinite Redirect (my fault) and the History Push Error seems to be resolved with the upgrades, so I can now at least demo the project. Again, the url is: https://wallzero.gitlab.io/ui-router-react-digest/

Now it seems ui-router does not work with the <base> tag - even if the <base> tag doesn't contain an href. With the <base> tag, every route transition triggers otherwise(). Both go() and a manual url will trigger otherwise(). In the demo you'll see it is stuck on taba. Selecting tabb fails to resolve the route and triggers otherwise(), making it return back to the resolve state, taba. I'm not sure why otherwise() is triggered, because if I call router.stateService.go() inside resolve(), it will complete without calling otherwise() again.

I've also noticed if I use a <base> tag, I have to remove the prepended / from my root route. If I don't, an extra / is shown after the <base>'s href - like in the demo.

If I run locally without the <base> tag, and return the prepended '/' to my root route, everything works properly. However, then I can no longer run on any path other than root /.

History Push Error

After upgrading ui-router-react from 0.3.0 to 0.4.0 and ui-router-core from ^1.0.0-beta.2 to 4.0.0, I am getting the following error messages when using router.stateService.go(statename) or router.urlRouter.otherwise(statename):

Chrome

Uncaught DOMException: Failed to execute 'replaceState' on 'History': A history state object with URL 'http://taba/' cannot be created in a document with origin 'http://localhost:8090' and URL 'http://localhost:8090/'.

Firefox

SecurityError: The operation is insecure.

I am unable to share a demo on GitLab due to non-root base causing infinite redirect, but you can clone the project and run locally yourself. Run via npm install && npm run build:demo && npm run start:demo:dev.

If you go to the localhost:8090; the attempted redirect from / to /taba will cause the error. If you manually go to localhost:8090/taba, you will see the states properly render. However, selecting a tab will also trigger the error, and the URL will not change.

Suggestion: inverted glob patterns in transition hooks

Currently, if I wanted to write a transition hook for a specific state, I'd target this state like so:

{ to: 'mystate' }

But if I needed a transition for every state but a specific one:

{ to: function(state) { return state.name != 'mystate' } }

Ideally, the glob pattern matcher would accept something like:

{ to: '!mystate' }  // Reject 'mystate'

This would allow interesting things like ignoring a whole path in the tree:

{ to: '!mystate.**' } // Match anything that is NOT under 'mystate'

What do you think?

Implicit any breaks builds

Since the new core is written completely in TypeScript, there would be no need for any external typings anymore, allowing the api changes to be in the type definitions at the time of release as well, but the problem is, that no noImplicitAny checks nor strictNullChecks are done by the typescript compiler atm. This breaks the build for everyone using this and making it impossible to use the integrated type definitions at the time.

Core should provide a standard `views:` StateBuilder

Currently each framework implementation is copying code for the basics of creating a views: builder. The core should provide a views builder which does such things as transforming

state: { 
  name: 'foo',
  component: FooComponent 
}

to (essentially):

state: { 
  name: 'foo',
  views: {
    $default: { 
      component: FooComponent
    }
  }
}

BREAKING CHANGE: Refactor URL encoding/decoding and string types

The URL parsing code currently has some vestiges from when it only worked with the angular 1 $location service.

The ng1 $location service has one major flaw we could never work around. This was the reason the custom ~2F encoding was created (when encoding parameter values with slashes in them).

Goals:

  • ui-router-core should not have special handling for encoding slashes
  • angular-ui-router itself should encode slashes as ~2F
    • only in path parameter values
    • The string type currently process ~2F; rename this type to path and make it the default parameter type for path parameters in ng1 only
  • Move LocationService and LocationConfig from coreservices to UIRouter instance

BREAKING CHANGE

  1. Slashes in param values no longer encode to ~2F except for ng1 path params
  2. coreservices.location moved to UIRouter.location.service and UIRouter.location.config

Discussion: Match the most specific URL, not the first found

Currently if you register states with urls like:

.state('foo', {
  url: '/foo/:fooid',
  params: { bar: null, squash: true }
})
.state('foo.bar', {
  url: '/staticstring'
})
.state('foo2', {
  url: '/foo/otherstring'
})

With URL /foo/staticstring, foo is matched because it matches the pattern and its url rule was registered first. Currently, the first matched URL always wins.

Likewise, with URL /foo/otherstring, foo is matched for the same reasons.

Choose most specific match

Consider implementing heuristics where the most specific match is chosen.

  • For url: /foo/staticstringwe should detect that foo.bar is more specific than foo.
    • Deepest state wins
  • For url: /foo/otherstring we should detect that foo2 is more specific than foo.
    • Fewer parameters win?

This would have the additional benefit during lazy loading. When states are lazy loaded, there might not be a guarantee of the load order, so less specific urls could be registered before more specific ones.

Plugin architecture

With visualizer, sticky states, dsr, etc. it might be nice to have an official plugin architecture.

Something like:

import {MyCoolPlugin} from "ui-router-cool-plugin";

var router = new UIRouterReact();
router.plugin(MyCoolPlugin);

[URL Matching by priority] inconsistent result in firefox

Given the following routes:

{ url: '/', name: 'home', component: ... }
{ url: '/{page}', name: 'cms', component: ... }

when visiting the home page in firefox the url match cms rule first but it should be 'home'

Workaround:

- { url: '/{page}', name: 'cms', component: ... }
+ { url: '/{page}', name: 'cms', params: { page: { value: '' } }, }

_.extend implementation breaks in IE11 when string is passed in

This manifests for me when running version 1.0.3 in an angular 1.x app, with e.g. states:

var states = [{
    name: 'liheap',
    url: '/liheap',
    parent: 'app',
    template: '<ui-view></ui-view>',
    redirectTo: 'liheap.vendor'
  }, {
    name: 'liheap.vendor',
    url: '/vendors',
    template: '<ui-view></ui-view>',
    redirectTo: 'liheap.vendor.list'
  }, {
    name: 'liheap.vendor.list',
    ...
  }];

On the transition to 'liheap.vendor.list', it throws a
Object.keys: argument is not an Object

on line https://github.com/ui-router/core/blob/master/src/common/common.ts#L579 where the second argument is a string, which causes the transition to fail.

I believe the root cause is discussed here: https://stackoverflow.com/questions/31917961/object-keys-behavior-in-chrome-and-ie-11

AoT compatible with Angular CLI

Hi! Is ui-router compatible with AoT compiler from CLI?

@NgModule({
  imports: [
    BrowserModule,    
    UIRouterModule.forRoot({     
      otherwise: { state: 'public.home', params: {} },
      useHash: false,
      //configClass: MyUIRouterConfig
    }),
  ],
  declarations: [
    AppComponent
  ],
  providers: [
  ],
  bootstrap: [ UIView ]
})
export class AppModule {}

I get this error:
image

State objects cannot be classes

In angular-ui-router 0.x you could create a class for a state:

class ModalState implements StateDeclaration {
  constructor(def) {
    Object.assign(this, def);
  }
  onEnter() {
    this.modalInstance = $uibModal.open(...);
    thos.modalInstance.result.then(this.onClose.bind(this), this.onDismiss.bind(this));
  }
}

This worked fine back then because the internal State object's prototype was the user supplied StateDeclaration, i.e,

var internalState = Object.create(stateDeclaration)`

but this is no longer possible because we now use new State() to get the State (class) prototype, i.e.,

var internalState = Object.assign(new State(), stateDeclaration);

Since Object.assign only makes copies of the stateDeclaration's own properties, it does not make copies of the prototype onEnter function, for example.


We should switch back to the old behavior (internal state object's prototype is the user supplied state declaration). We will lose the ability to do obj instanceof State, but that is a small price to enable numerous other convenient features.

For example, hook functions can check for user-supplied state properties without explicitly querying state.self.property:

transitionService.onStart({ to: state => state.customflag }, ...)

Projects like sticky states can define new callbacks (like onInactivate) without also creating a statebuilder to enable access to the stateDeclaration.onInactivate property.

null parameter value does not work when inherit is true

In some cases, a null query parameter value does not take effect. When a query parameter has a value, and is updated to be 'null', it may not take effect.

Here is a failing unit test:

    it('should not inherit previous params when new params are passed', async(done) => {
      router.stateRegistry.register({
        name: 'foo',
        url: '?fooParam',
      });

      await $state.go('foo', { fooParam: 'abc' });
      expect(router.globals.params).toEqualValues({ fooParam: 'abc' });

      await $state.go('foo', { fooParam: undefined });
      expect(router.globals.params).toEqualValues({ fooParam: null });

      done();
    });
FAILED transition inherited params should not inherit previous params when new params are passed
debug.js:21 Uncaught Expected StateParams({ #: null, fooParam: 'abc' }) to equal values Object({ fooParam: null }).
    at Suite.<anonymous> (http://localhost:8080/base/test/index.js:20452:56)
    at step (http://localhost:8080/base/test/index.js:19674:24)
    at Object.next (http://localhost:8080/base/test/index.js:19655:54)
    at fulfilled (http://localhost:8080/base/test/index.js:19646:59)

HookMatchCriteria compile error in TypeScript with strict null checks

Compiling a typescript application with strict null checks enabled will produce a number of errors due to the definition of HookMatchCriteria:

ERROR in /home/schoel/Projekte/javascript/i3ge/node_modules/ui-router-core/lib/transition/interface.d.ts
(735,5): error TS2411: Property 'to' of type 'string | boolean | Predicate | undefined' is not assignable to string index type 'string | boolean | Predicate'.

ERROR in /home/schoel/Projekte/javascript/i3ge/node_modules/ui-router-core/lib/transition/interface.d.ts
(737,5): error TS2411: Property 'from' of type 'string | boolean | Predicate | undefined' is not assignable to string index type 'string | boolean | Predicate'.

ERROR in /home/schoel/Projekte/javascript/i3ge/node_modules/ui-router-core/lib/transition/interface.d.ts
(739,5): error TS2411: Property 'exiting' of type 'string | boolean | Predicate | undefined' is not assignable to string index type 'string | boolean | Predicate'.

ERROR in /home/schoel/Projekte/javascript/i3ge/node_modules/ui-router-core/lib/transition/interface.d.ts
(741,5): error TS2411: Property 'retained' of type 'string | boolean | Predicate | undefined' is not assignable to string index type 'string | boolean | Predicate'.

ERROR in /home/schoel/Projekte/javascript/i3ge/node_modules/ui-router-core/lib/transition/interface.d.ts
(743,5): error TS2411: Property 'entering' of type 'string | boolean | Predicate | undefined' is not assignable to string index type 'string | boolean | Predicate'.

This is because the type of the string keyed property ([key: string]: HookMatchCriterion) has to match the type any other property in the interface. All other properties are optional, though, and thus of type HookMatchCriterion | undefined. Changing the type of the string keyed property to HookMatchCriterion | undefined resolves the issue.

Performance of new Order URL Matching Rules by priority, not registration order

The new matching functions seem to have some serious performance impact if you use many states:

Plunkr with 500 states:

Plunkr: pre 3.0.0 (beta.3) Fast URL match: angular.run reached in 2 seconds
http://plnkr.co/edit/BXsCPsh5qLiVatmavtiO?p=preview

Plunkr: 3.0.0 (rc.1) Slow URL match: angular.run reached in 25 seconds
http://plnkr.co/edit/0vb4ReYiabWsZprCrk4F?p=preview

I do use a lot of states (angular 1, 0.3.X migration) without priority and noticed this performance issue in my application.

Suggest location: 'replace' on redirectTo behavior

In UI Router < version 1.x, I had implemented my own redirectTo functionality in a $stateChangeStart event. One thing that I added was a 'replace' for the location parameter. eg.
$state.go(toState.redirectTo, toStateParams, {location: 'replace'});
The benefit of this was to avoid the odd glitch in browser history.

For eg. when a user navigates: was on state1 -> (navigates) -> state 2 -> (redirect occurs) -> state3
Then they click the back button in the browser.
With location: 'replace', they are taken to state 1, and skip state 2, which I think is a better experience.

With the built in redirectTo in UI Router version 1.x, this doesn't seem to be the case.
Clicking the back button causes a brief "blip", taking the user to state 2, and quickly redirecting back to state 3.
Upon clicking it again, only then are you taken to state 1 as expected.

Are there any plans to improve this? Or is there a work around to cause the "redirectTo" to perform a location: 'replace'?

Browser Back Button click reloading the wrong state with wrong param value in chrome

Hi
we are moving to ui-router 1.0 and having issue one of browser back button click.
Here is my scenario -

  1. user clicks a link and it goes to a state A ( let's say url http://testApp.com/#/events/all)

  2. Inside State A, user clicks one more link and It goes to State B.
    url -> http://testApp.com/#/events/<<idParam>>

    • State B needs a param id
    • State B has onEnter and onExit
    • In side onEnter, it's being redirected to another state using
      $transition$.router.stateService.target('<<State C Name>>', <<State B Param>>)
      redirection happens on a condition which is true in this case.
  3. So, the last state it directs to State C and brower url showing state C url ->
    http://testApp.com/#/events/<<id>>/View

  4. As per user, url changes from State A to State C. So, when user clicks back button, he/she should go back to State A

  5. to handle the browser back button click, we have implemented
    $rootScope.$on('$locationChangeStart', (event, newUrl) => {<<logic to show confirmation pop up>>});

  6. when code is triggering, the newUrl (above Step 5 parameter) is showing as http://testApp.com/#/events/all ( step 1 url) which is correct but some how the State B is getting loaded again and it's taking all as id parameter and calling resolve methods in StateB and failing because all is not a valid param value.

StateB has an onExit Method and I think only that should be executed not the resolve methods. and State A should be loaded but it is not.

I am suspecting issue in $transition$.router.stateService.target which is doing redirection in onEnter (step 2 above). I tried to use location = 'replace' option but did not work.

NOTE This is only happening in chrome. Firefox is working as expected. Firefox going back from StateC to State A without any issue.

I am stuck here and any help would be highly appreciated.

Double onEnter = Param values not valid for state ?

Hello !

context

I have one state A, a child B and a child C:

A -> B -> C

In state A declaration:

name: 'organizations',
url: '/organizations',
onEnter: (trans, state) => {
      if (trans.params().organization_id) {
        return;
      }

      return trans.injector().getAsync('organizations').then(organizations => {
        const organization_id = get(organizations, '[0].organization_id');
        if (!organization_id) {
          return;
        }

          stateService.go(applications.state, {organization_id}); // will work
      });
    },

In state A.B declaration:

parent: organizationsState,
name: 'applications',
url: '/:organization_id/applications',
onEnter: (trans) => {
      if (trans.params().application_id) {
        return;
      }

      return trans.injector().getAsync('applications').then(applications => {
        const application_id = get(applications, '[0].application_id');
        if (!application_id) {
          return;
        }

        stateService.go(sessions.state, {application_id}); // will throw a "Param values not valid for state" even if application_id is perfectly valid.
      });
    },

In state A.B.C declaration:

parent: applicationState,
name: 'sessions',
url: '/:application_id/sessions',

// no params:{} or thing like that

issue

When transitioning from onEnter to onEnter callback using stateService.go() the second call will throw a "Param values not valid for state" even if application_id is perfectly valid.

workaround

The workaround is to defer stateService.go(sessions.state, {application_id}); with setTimeout(() => stateService.go(sessions.state, {application_id})) for instance.

Custom parameter type’s encode function is not called for array params

The problem

  1. Define a custom type using the definitionFn instead of the definition object
  2. Use this new type for a state parameter that does not explicitly set array: false
  3. The parameter type’s encode function is never invoked during state transitions, so the parameter is not encoded correctly.

The reason
Because the parameter does not declare array: false its type is wrapped into an ArrayType during state declaration – that’s before the type’s definitionFn is invoked.

But ArrayType defines its encode function (and others) based on the wrapped type’s functions at the time when ArrayType is instantiated. ArrayType stores a reference to the old encode function (which was undefined, so it’s the identity function) instead of looking up the actual function when ArrayType.encode is called.

Later, the type’s definitionFn is invoked and redefines the encode function, but the array params still reference the identity function.

The solution

  • Either defer the instantiation of ArrayType until after all definitionFns were invoked (probably compilcated)
  • Or define the ArrayType’s functions based on the run-time value of the wrapped type’s functions instead of capturing them locally.

[Future State] match url with glob patterns

Before:

{ name: 'auth.login.**', url: '/login', lazyLoad: ... },
{ name: 'auth.signup.**', url: '/signup', lazyLoad: ... },

After:

{ name: 'auth.**', url: '/(login|signup)', lazyLoad: ... },

If a transition to a particular state+params is pending, attempting to go to that state+params again should be a no-op

In the current codebase, attempting to go to a state+params identical to your current state+params is a no-op. This is good and makes sense, since there's nothing meaningful to do to transition from your current state+params to your current state+params.

However if you attempt to go to a different state+params, such as by clicking on a ui-sref, a transition is always created and ui-router starts resolving the necessary data. This causes a problem if the user tries to click the same ui-sref twice or more - ui-router will queue up a bunch of identical transitions, waste time retrieving the same resolve data repeatedly, and take much longer to actually switch to the new state.

A request to transition to a state should therefore be ignored if either the requested state is exactly the current state or it is exactly the destination state for the current transition.

Expose API to imperatively lazy load states

Currently the only way to lazy load a future state is to activate the state. The lazyLoad transition hook handles this.

If a developer wants to preload lazy routes, they cannot.

We could expose an API to imperatively trigger a state's lazyload function

Transition hook criteria `from` not respecting empty string

From @adamreisnz on January 19, 2017 22:43

I want to trigger a transition hook which only fires if the user enters a specific state without having a from state, e.g. they land on the page by typing in the URL or following a direct link.

I assumed this would be accomplished via:

$transitionsProvider.onBefore({
    from: '',
    to: 'welcome.*',
  }, transition => { ... });

However, that seems to capture any transition to any welcome.* route, as I suspect the empty from value is being ignored.

Is there another way to have hook criteria match only when there isn't any from state?
If not, can it be implemented so that an empty string (as opposed to undefined / null) triggers this check?

The docs don't document this use case either.

Copied from original issue: angular-ui/ui-router#3285

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.