From bc17066b00ea049b5ae92e577ebb8638b770d077 Mon Sep 17 00:00:00 2001 From: Chris Thielen Date: Sun, 4 Sep 2016 13:12:51 -0500 Subject: [PATCH] feat(ng2): Improve ng2 bootstrap flexibility with provideUIRouter() provider factory function fix(ng2): Update deps to rc.6 Closes #2958 --- package.json | 16 +++++------ packages/ng2/package.json | 2 +- src/ng2/directives/uiView.ts | 2 +- src/ng2/interface.ts | 2 +- src/ng2/location.ts | 4 +-- src/ng2/providers.ts | 53 ++++++++++++++++++++++++++++++------ src/ng2/uiRouterNgModule.ts | 38 ++++++++++++++++++-------- 7 files changed, 84 insertions(+), 33 deletions(-) diff --git a/package.json b/package.json index 0c59bfdbf..afa0460ed 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "ui-router", "description": "State-based routing for Javascript", - "version": "1.0.0-beta.1", + "version": "1.0.0-beta.2", "scripts": { "test": "npm run test:integrate", "watch": "node_modules/watch/cli.js 'npm run test' src test", @@ -56,11 +56,11 @@ }, "license": "MIT", "devDependencies": { - "@angular/common": "=2.0.0-rc.5", - "@angular/compiler": "=2.0.0-rc.5", - "@angular/core": "=2.0.0-rc.5", - "@angular/platform-browser": "=2.0.0-rc.5", - "@angular/platform-browser-dynamic": "=2.0.0-rc.5", + "@angular/common": "=2.0.0-rc.6", + "@angular/compiler": "=2.0.0-rc.6", + "@angular/core": "=2.0.0-rc.6", + "@angular/platform-browser": "=2.0.0-rc.6", + "@angular/platform-browser-dynamic": "=2.0.0-rc.6", "babel-core": "^5.8.14", "clone": "^1.0.2", "conventional-changelog": "^1.1.0", @@ -82,7 +82,7 @@ "phantomjs-polyfill": "0.0.1", "reflect-metadata": "=0.1.2", "remap-istanbul": "^0.6.3", - "rxjs": "=5.0.0-beta.6", + "rxjs": "^5.0.0-beta.11", "shelljs": "^0.7.0", "systemjs": "^0.18.4", "ts-loader": "^0.8.1", @@ -94,6 +94,6 @@ "webpack": "1.x", "webpack-dev-server": "1.x", "yargs": "^4.2.0", - "zone.js": "=0.6.12" + "zone.js": "^0.6.17" } } diff --git a/packages/ng2/package.json b/packages/ng2/package.json index e1fe1f4aa..01f6fecc4 100644 --- a/packages/ng2/package.json +++ b/packages/ng2/package.json @@ -2,7 +2,7 @@ "name": "ui-router-ng2", "description": "State-based routing for Angular 2", "peerDependencies": { - "@angular/core": "^2.0.0-rc.5" + "@angular/core": "^2.0.0-rc.6" }, "main": "ng2.js", "typings": "ng2.d.ts" diff --git a/src/ng2/directives/uiView.ts b/src/ng2/directives/uiView.ts index 3b95f3d02..807bcd953 100755 --- a/src/ng2/directives/uiView.ts +++ b/src/ng2/directives/uiView.ts @@ -30,7 +30,7 @@ interface InputMapping { declare var Reflect: any; /** @hidden */ -const ng2ComponentInputs = (ng2CompClass: Type) => { +const ng2ComponentInputs = (ng2CompClass: Type) => { /** Get "@Input('foo') _foo" inputs */ let props = Reflect['getMetadata']('propMetadata', ng2CompClass); let _props = Object.keys(props || {}) diff --git a/src/ng2/interface.ts b/src/ng2/interface.ts index 30d20fcd9..68ab62a8c 100644 --- a/src/ng2/interface.ts +++ b/src/ng2/interface.ts @@ -223,7 +223,7 @@ export interface Ng2ViewDeclaration extends _ViewDeclaration { * } * ``` */ - component?: Type; + component?: Type; /** * An object which maps `resolve` keys to [[component]] `bindings`. diff --git a/src/ng2/location.ts b/src/ng2/location.ts index fe5b2fece..2636a7b8e 100644 --- a/src/ng2/location.ts +++ b/src/ng2/location.ts @@ -1,5 +1,5 @@ /** @module ng2 */ /** */ -import {HashLocationStrategy, PlatformLocation, LocationStrategy, UrlChangeListener} from "@angular/common"; +import {HashLocationStrategy, PlatformLocation, LocationStrategy, LocationChangeListener} from "@angular/common"; import {Injectable} from "@angular/core"; import {services} from "../common/coreservices"; @@ -60,7 +60,7 @@ export class UIRouterLocation { console.log(new Error('$location.replace() not impl')) }; - loc.onChange = (cb: UrlChangeListener) => locSt.onPopState(cb); + loc.onChange = (cb: LocationChangeListener) => locSt.onPopState(cb); let locCfg = services.locationConfig; diff --git a/src/ng2/providers.ts b/src/ng2/providers.ts index 91226fe03..0921363dd 100644 --- a/src/ng2/providers.ts +++ b/src/ng2/providers.ts @@ -46,7 +46,8 @@ * * @preferred @module ng2 */ /** */ -import {Injector, OpaqueToken} from "@angular/core"; +import {Injector, OpaqueToken, Provider} from "@angular/core"; +import {ClassProvider, ExistingProvider, FactoryProvider, TypeProvider, ValueProvider} from "@angular/core"; // has or is using import {UIRouter} from "../router"; import {PathNode} from "../path/node"; import {StateRegistry} from "../state/stateRegistry"; @@ -62,17 +63,20 @@ import {UIRouterConfig} from "./uiRouterConfig"; import {Globals} from "../globals"; import {UIRouterLocation} from "./location"; import {services} from "../common/coreservices"; -import {ProviderLike} from "../state/interface"; import {Resolvable} from "../resolve/resolvable"; import {ngModuleResolvablesBuilder} from "./statebuilders/lazyLoadNgModuleResolvable"; import {flattenR} from "../common/common"; import {UIROUTER_STATES_TOKEN} from "./uiRouterNgModule"; import {UIRouterRx} from "./rx"; +import {LocationStrategy, HashLocationStrategy, PathLocationStrategy} from "@angular/common"; export const NG1_UIROUTER_TOKEN = new OpaqueToken("$uiRouter"); /** - * This is a provider factory for a UIRouter instance which is configured for Angular 2 + * This is a factory function for a UIRouter instance + * + * Creates a UIRouter instance and configures it for Angular 2, then invokes router bootstrap. + * This function is used as an Angular 2 `useFactory` Provider. */ let uiRouterFactory = (injector: Injector) => { // ----------------- ng1-to-ng2 short circuit ------ @@ -140,10 +144,12 @@ let uiRouterFactory = (injector: Injector) => { return router; }; -export const _UIROUTER_PROVIDERS: ProviderLike[] = [ - { provide: UIRouterLocation, useClass: UIRouterLocation }, +export const _UIROUTER_INSTANCE_PROVIDERS: Provider[] = [ { provide: UIRouter, useFactory: uiRouterFactory, deps: [Injector] }, + { provide: UIRouterLocation, useClass: UIRouterLocation }, +]; +export const _UIROUTER_PROVIDERS: Provider[] = [ { provide: StateService, useFactory: (r: UIRouter) => r.stateService , deps: [UIRouter]}, { provide: TransitionService, useFactory: (r: UIRouter) => r.transitionService, deps: [UIRouter]}, { provide: UrlMatcherFactory, useFactory: (r: UIRouter) => r.urlMatcherFactory, deps: [UIRouter]}, @@ -153,10 +159,41 @@ export const _UIROUTER_PROVIDERS: ProviderLike[] = [ { provide: Globals, useFactory: (r: UIRouter) => r.globals , deps: [UIRouter]}, { provide: UIView.PARENT_INJECT, useFactory: (r: StateRegistry) => { return { fqn: null, context: r.root() } as ParentUIViewInject }, deps: [StateRegistry]} -] +]; + +/** + * Provides an Instance of UI-Router for NG2. + * + * Use this on the root NgModule to configure and create an instance of the Angular 2 UIRouter. + * + * @example + * ```js + * + * @UIRouterModule({ + * states: [ homeState, aboutState ], + * providers: [ provideUIRouter({ configClass: MyUIRouterConfig, useHash: true }) ], + * bootstrap: [ UIView ] + * }) class RootNgModule {} + * + * platformBrowserDynamic().bootstrapModule(RootNgModule); + * ``` + * + * Note: UIRouter should only be provided *once*, on the root module, when bootstrapping the application. + */ +export function provideUIRouter(rootConfig: { configClass?: typeof UIRouterConfig, useHash?: boolean } = {}) { + // Provide the UIRouter instance providers + return _UIROUTER_INSTANCE_PROVIDERS.concat( + // Provide the user-supplied UIRouterConfig class, or use base UIRouterConfig (as a no-op config) + { provide: UIRouterConfig, useClass: (rootConfig.configClass as any || UIRouterConfig) }, + // Provide the PathLocationStrategy by default unless `useHash` is `true` + { provide: LocationStrategy, useClass: (rootConfig.useHash ? HashLocationStrategy : PathLocationStrategy ) } + ); +} + /** * The UI-Router providers, for use in your application bootstrap * - * @deprecated use [[UIRouterModule]] + * @deprecated use [[UIRouterModule]] and [[provideUIRouter]] */ -export const UIROUTER_PROVIDERS = _UIROUTER_PROVIDERS; +export const UIROUTER_PROVIDERS: Provider[] = _UIROUTER_INSTANCE_PROVIDERS.concat(_UIROUTER_PROVIDERS); + diff --git a/src/ng2/uiRouterNgModule.ts b/src/ng2/uiRouterNgModule.ts index 106d8ed3b..aeccb0984 100644 --- a/src/ng2/uiRouterNgModule.ts +++ b/src/ng2/uiRouterNgModule.ts @@ -11,7 +11,7 @@ import {uniqR, flattenR} from "../common/common"; entryComponents: [UIView], providers: [_UIROUTER_PROVIDERS] }) -export class UIRouterRootModule {} +export class UIRouterLibraryModule {} /** * A module declaration lteral, including UI-Router states. @@ -28,11 +28,23 @@ export const UIROUTER_STATES_TOKEN = new OpaqueToken("UIRouter States"); /** * Declares a NgModule with UI-Router states * - * A Typescript decorator for declaring a [NgModule](https://angular.io/docs/ts/latest/guide/ngmodule.html) - * which contains UI-Router states. + * A Typescript decorator for declaring an [NgModule](https://angular.io/docs/ts/latest/guide/ngmodule.html) + * which contains UI-Router states and/or uses UI-Router directives and providers. + * + * The decorator adds the `UIRouterLibraryModule` NgModule as an import. + * The `UIRouterLibraryModule has the UI-Router directives and providers. * - * This decorator analyzes the `states` in the module. - * It adds all routed `component:`(s) for each state to the module's `declarations` and `entryComponents`. + * The decorator also analyzes the `states:` property. + * When it finds a state with a routed `component:`, it adds the component + * to the module's `declarations` and `entryComponents`. + * + * Note: adding the component to `entryComponents` instructs the Module Compiler that those + * components should be compiled. + * Otherwise, they would not be automatically discovered as "reachable" by the compiler. + * + * Further, the states found in the `states:` property are added to Dependency Injection + * using a specific token. + * This will automatically register them with the [[StateRegistry]] when the application bootstraps. * * @example * ```js @@ -41,21 +53,23 @@ export const UIROUTER_STATES_TOKEN = new OpaqueToken("UIRouter States"); * var aboutState = { name: 'about', url: '/about', component: About }; * * @UIRouterModule({ - * imports: [BrowserModule], - * declarations: [NonRoutedComponent], - * states: [homeState, aboutState] + * imports: [ BrowserModule ], + * declarations: [ NonRoutedComponent ], + * states: [ homeState, aboutState ] * }) export class AppModule {}; * ``` * - * The `UIRouterModule` decorator creates an Angular 2 `NgModule`. - * The equivalent `AppModule` could also be crafted by hand using the `NgModule` decorator: + * --- + * + * Note: the `UIRouterModule` decorator creates a standard Angular 2 `NgModule`. + * The equivalent `AppModule` could also be crafted by hand using only the `NgModule` decorator: * * ``` * var homeState = { name: 'home', url: '/home', component: Home }; * var aboutState = { name: 'about', url: '/about', component: About }; * * @NgModule({ - * imports: [BrowserModule, UIRouterRootModule], + * imports: [BrowserModule, UIRouterLibraryModule], * declarations: [NonRoutedComponent, Home, About], * entryComponents: [Home, About], * providers: [ @@ -79,7 +93,7 @@ export function UIRouterModule(moduleMetaData: UIRouterModuleMetadata) { .reduce((acc, arr) => acc.concat(arr), []) .filter(x => typeof x === 'function' && x !== UIView); - moduleMetaData.imports = (moduleMetaData.imports || []).concat(UIRouterRootModule).reduce(uniqR, []); + moduleMetaData.imports = (moduleMetaData.imports || []).concat(UIRouterLibraryModule).reduce(uniqR, []); moduleMetaData.declarations = (moduleMetaData.declarations || []).concat(routedComponents).reduce(uniqR, []); moduleMetaData.entryComponents = (moduleMetaData.entryComponents || []).concat(routedComponents).reduce(uniqR, []); moduleMetaData.providers = (moduleMetaData.providers || []).concat(statesProvider);