From 33ae6a8239a3590ca4ed5fc9e274dd45e4e601b9 Mon Sep 17 00:00:00 2001 From: Chris Thielen Date: Sat, 14 Jan 2017 13:28:26 -0600 Subject: [PATCH] fix(location): make initial otherwise('/') activate state (perform url sync) refactor(location) move location code to /location/* refactor(plugins): Switch to plugins API feat(UrlService): Add UrlService injectable Closes #17 --- package.json | 8 +-- src/ng2.ts | 3 +- src/ng2/location.ts | 79 ----------------------- src/ng2/location/locationConfig.ts | 27 ++++++++ src/ng2/location/locationService.ts | 33 ++++++++++ src/ng2/location/uiRouterLocation.ts | 20 ++++++ src/ng2/providers.ts | 93 ++++++++++++++-------------- 7 files changed, 131 insertions(+), 132 deletions(-) delete mode 100644 src/ng2/location.ts create mode 100644 src/ng2/location/locationConfig.ts create mode 100644 src/ng2/location/locationService.ts create mode 100644 src/ng2/location/uiRouterLocation.ts diff --git a/package.json b/package.json index 474af1a29..db1667f88 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "version": "1.0.0-beta.4", "scripts": { "clean": "shx rm -rf lib lib-esm _bundles _doc", - "build": "npm run clean && node_modules/.bin/ngc && node_modules/.bin/ngc -p tsconfig.esm.json && webpack", + "build": "npm run clean && ngc && ngc -m es6 -outDir lib-esm && webpack", "test": "karma start config/karma.ng2.js", "docs": "typedoc --tsconfig tsconfig.typedoc.json --readme README.md --name 'ui-router-ng2' --theme node_modules/ui-router-typedoc-themes/bin/default --out _doc --internal-aliases internal,coreapi,ng2api --external-aliases internalapi,external --navigation-label-globals ui-router-ng2" }, @@ -49,11 +49,11 @@ "main": "lib/ng2.js", "typings": "lib/ng2.d.ts", "dependencies": { - "ui-router-core": "=1.0.1" + "ui-router-core": "=3.1.0" }, "peerDependencies": { - "@angular/core": "^2.0.0", - "@angular/common": "^2.0.0" + "@angular/common": "^2.0.0", + "@angular/core": "^2.0.0" }, "devDependencies": { "@angular/common": "^2.3.1", diff --git a/src/ng2.ts b/src/ng2.ts index e44188599..69ffb7c0c 100644 --- a/src/ng2.ts +++ b/src/ng2.ts @@ -1,6 +1,5 @@ /** @ng2api @module ng2 */ /** for typedoc */ export * from "ui-router-core"; -import "ui-router-core/lib/justjs"; import 'rxjs/add/observable/of'; import 'rxjs/add/observable/combineLatest'; @@ -14,7 +13,7 @@ export * from "./ng2/interface"; export * from "./ng2/lazyLoadNgModule"; export * from "./ng2/rx"; export * from "./ng2/providers"; -export * from "./ng2/location"; +export * from "./ng2/location/uiRouterLocation"; export * from "./ng2/directives/directives"; export * from "./ng2/statebuilders/views"; export * from "./ng2/uiRouterNgModule"; diff --git a/src/ng2/location.ts b/src/ng2/location.ts deleted file mode 100644 index 83e3ae4c6..000000000 --- a/src/ng2/location.ts +++ /dev/null @@ -1,79 +0,0 @@ -/** @module ng2 */ /** */ -import {HashLocationStrategy, PlatformLocation, LocationStrategy, LocationChangeListener} from "@angular/common"; -import {Injectable} from "@angular/core"; - -import {services} from "ui-router-core"; -import {isDefined} from "ui-router-core"; -import {applyPairs} from "ui-router-core"; -import {beforeAfterSubstr} from "ui-router-core"; - -const splitOnHash = beforeAfterSubstr("#"); -const splitOnEquals = beforeAfterSubstr("="); -const splitOnQuestionMark = beforeAfterSubstr("?"); - -@Injectable() -export class UIRouterLocation { - isHashBang: boolean; - hashPrefix: string = ""; - - constructor( - public locationStrategy: LocationStrategy, - public platformLocation: PlatformLocation - ) { - this.isHashBang = locationStrategy instanceof HashLocationStrategy; - } - - init() { - let loc = services.location; - let locSt = this.locationStrategy; - - if (this.isHashBang) { - loc.path = () => - splitOnHash(splitOnQuestionMark(locSt.path())[0])[0]; - loc.hash = () => - splitOnHash(splitOnHash(this.platformLocation.hash)[1])[1]; - } else { - let basepath = locSt.getBaseHref(); - let basepathRegExp = new RegExp("^" + basepath); - let replace = (basepath[basepath.length - 1] === '/') ? "/" : ""; - loc.path = () => - splitOnHash(splitOnQuestionMark(locSt.path())[0])[0].replace(basepathRegExp, replace); - loc.hash = () => - splitOnHash(this.platformLocation.hash)[1]; - } - - - loc.search = (() => { - let queryString = splitOnHash(splitOnQuestionMark(locSt.path())[1])[0]; - return queryString.split("&").map(kv => splitOnEquals(kv)).reduce(applyPairs, {}); - }); - - loc.setUrl = (url: string, replace: boolean = false) => { - if(isDefined(url)) { - let split = splitOnQuestionMark(url); - if (replace) { - locSt.replaceState(null, null, split[0], split[1]); - } else { - locSt.pushState(null, null, split[0], split[1]); - } - } - }; - - loc.onChange = (cb: LocationChangeListener) => locSt.onPopState(cb) as any; - - let locCfg = services.locationConfig; - - locCfg.port = () => null as number; - locCfg.protocol = () => null as string; - locCfg.host = () => null as string; - locCfg.baseHref = () => locSt.getBaseHref(); - locCfg.html5Mode = () => !this.isHashBang; - locCfg.hashPrefix = (newprefix: string): string => { - if(isDefined(newprefix)) { - this.hashPrefix = newprefix; - } - return this.hashPrefix; - }; - } -} - diff --git a/src/ng2/location/locationConfig.ts b/src/ng2/location/locationConfig.ts new file mode 100644 index 000000000..43f88073c --- /dev/null +++ b/src/ng2/location/locationConfig.ts @@ -0,0 +1,27 @@ +/** @module ng2 */ +/** */ + +import { UIRouter, is, isDefined } from "ui-router-core"; +import { PlatformLocation, LocationStrategy, PathLocationStrategy } from "@angular/common"; + +export class Ng2LocationConfig { + private _isHtml5: boolean; + private _hashPrefix: string = ""; + + constructor(router: UIRouter, locationStrategy: LocationStrategy, public platformLocation: PlatformLocation) { + this._isHtml5 = is(PathLocationStrategy)(locationStrategy); + } + + dispose() {} + port = () => null as number; + protocol = () => null as string; + host = () => null as string; + baseHref = () => this.platformLocation.getBaseHrefFromDOM(); + html5Mode = () => this._isHtml5; + hashPrefix = (newprefix?: string): string => { + if(isDefined(newprefix)) { + this._hashPrefix = newprefix; + } + return this._hashPrefix; + }; +} \ No newline at end of file diff --git a/src/ng2/location/locationService.ts b/src/ng2/location/locationService.ts new file mode 100644 index 000000000..8dd125f54 --- /dev/null +++ b/src/ng2/location/locationService.ts @@ -0,0 +1,33 @@ +/** @module ng2 */ +/** */ +import { UIRouter } from "ui-router-core"; +import { BaseLocationServices } from "ui-router-core/lib/vanilla"; +import { parseUrl } from "ui-router-core/lib/vanilla/utils"; +import { PlatformLocation, LocationStrategy } from "@angular/common"; + +/** A `LocationServices` that uses the browser hash "#" to get/set the current location */ +export class Ng2LocationServices extends BaseLocationServices { + constructor(router: UIRouter, private _locationStrategy: LocationStrategy, private _platform: PlatformLocation) { + super(router, true); + this._locationStrategy.onPopState(this._listener) + } + + _get() { + return this._locationStrategy.path(true); + } + + _set(state: any, title: string, url: string, replace: boolean): any { + let { path, search, hash } = parseUrl(url); + let urlWithHash = path + (hash ? "#" + hash : ""); + + if (replace) { + this._locationStrategy.replaceState(state, title, urlWithHash, search); + } else { + this._locationStrategy.pushState(state, title, urlWithHash, search); + } + } + + dispose (router: UIRouter) { + super.dispose(router); + } +} diff --git a/src/ng2/location/uiRouterLocation.ts b/src/ng2/location/uiRouterLocation.ts new file mode 100644 index 000000000..a1a08c78b --- /dev/null +++ b/src/ng2/location/uiRouterLocation.ts @@ -0,0 +1,20 @@ +/** @module ng2 */ +/** */ +import { PlatformLocation, LocationStrategy } from "@angular/common"; +import { Injectable } from "@angular/core"; +import { UIRouter } from "ui-router-core"; +import { Ng2LocationConfig } from "./locationConfig"; +import { Ng2LocationServices } from "./locationService"; + +@Injectable() +export class UIRouterLocation { + constructor(public locationStrategy: LocationStrategy, public platformLocation: PlatformLocation ) { } + + init(router: UIRouter) { + router.locationService = new Ng2LocationServices(router, this.locationStrategy, this.platformLocation); + router.locationConfig = new Ng2LocationConfig(router, this.locationStrategy, this.platformLocation) + } +} + + + diff --git a/src/ng2/providers.ts b/src/ng2/providers.ts index 057c99959..ee4aad12b 100644 --- a/src/ng2/providers.ts +++ b/src/ng2/providers.ts @@ -83,27 +83,21 @@ * ``` * * @preferred @module ng2 - */ /** */ -import {Injector, Provider} from "@angular/core"; -import {UIRouter} from "ui-router-core"; -import {PathNode} from "ui-router-core"; -import {StateRegistry} from "ui-router-core"; -import {StateService} from "ui-router-core"; -import {TransitionService} from "ui-router-core"; -import {UrlMatcherFactory} from "ui-router-core"; -import {UrlRouter} from "ui-router-core"; -import {ViewService} from "ui-router-core"; -import {UIView, ParentUIViewInject} from "./directives/uiView"; -import {ng2ViewsBuilder, Ng2ViewConfig} from "./statebuilders/views"; -import {Ng2ViewDeclaration} from "./interface"; -import {applyRootModuleConfig, applyModuleConfig} from "./uiRouterConfig"; -import {Globals} from "ui-router-core"; -import {UIRouterLocation} from "./location"; -import {services} from "ui-router-core"; -import {Resolvable} from "ui-router-core"; -import {RootModule, StatesModule, UIROUTER_ROOT_MODULE, UIROUTER_MODULE_TOKEN} from "./uiRouterNgModule"; -import {UIRouterRx} from "./rx"; -import {NATIVE_INJECTOR_TOKEN} from "ui-router-core"; + */ +/** */ +import { Injector, Provider } from "@angular/core"; +import { + UIRouter, PathNode, StateRegistry, StateService, TransitionService, UrlMatcherFactory, UrlRouter, ViewService, + UrlService, Globals, services, Resolvable, NATIVE_INJECTOR_TOKEN +} from "ui-router-core"; +import { UIView, ParentUIViewInject } from "./directives/uiView"; +import { ng2ViewsBuilder, Ng2ViewConfig } from "./statebuilders/views"; +import { Ng2ViewDeclaration } from "./interface"; +import { applyRootModuleConfig, applyModuleConfig } from "./uiRouterConfig"; +import { UIRouterLocation } from "./location/uiRouterLocation"; +import { RootModule, StatesModule, UIROUTER_ROOT_MODULE, UIROUTER_MODULE_TOKEN } from "./uiRouterNgModule"; +import { UIRouterRx } from "./rx"; +import { servicesPlugin } from "ui-router-core/lib/vanilla"; /** * This is a factory function for a UIRouter instance @@ -120,27 +114,33 @@ export function uiRouterFactory(location: UIRouterLocation, injector: Injector) throw new Error("Exactly one UIRouterModule.forRoot() should be in the bootstrapped app module's imports: []"); } - // ----------------- Monkey Patches ---------------- - // Monkey patch the services.$injector to the ng2 Injector - services.$injector.get = injector.get.bind(injector); - - // Monkey patch the services.$location with ng2 Location implementation - location.init(); - // ----------------- Create router ----------------- // Create a new ng2 UIRouter and configure it for ng2 let router = new UIRouter(); - new UIRouterRx(router); - let registry = router.stateRegistry; + + // Add RxJS plugin + router.plugin(UIRouterRx); + + // Add $q-like and $injector-like service APIs + router.plugin(servicesPlugin); + + + // ----------------- Monkey Patches ---------------- + // Monkey patch the services.$injector to use the root ng2 Injector + services.$injector.get = injector.get.bind(injector); + // ----------------- Configure for ng2 ------------- + location.init(router); + // Apply ng2 ui-view handling code - router.viewService.viewConfigFactory("ng2", (path: PathNode[], config: Ng2ViewDeclaration) => new Ng2ViewConfig(path, config)); - registry.decorator('views', ng2ViewsBuilder); + let viewConfigFactory = (path: PathNode[], config: Ng2ViewDeclaration) => new Ng2ViewConfig(path, config); + router.viewService._pluginapi._viewConfigFactory("ng2", viewConfigFactory); // Apply statebuilder decorator for ng2 NgModule registration - registry.stateQueue.flush(router.stateService); + let registry = router.stateRegistry; + registry.decorator('views', ng2ViewsBuilder); // Prep the tree of NgModule by placing the root NgModule's Injector on the root state. let ng2InjectorResolvable = Resolvable.fromData(NATIVE_INJECTOR_TOKEN, injector); @@ -148,22 +148,19 @@ export function uiRouterFactory(location: UIRouterLocation, injector: Injector) // ----------------- Initialize router ------------- - // Allow states to be registered - registry.stateQueue.autoFlush(router.stateService); - setTimeout(() => { rootModules.forEach(moduleConfig => applyRootModuleConfig(router, injector, moduleConfig)); modules.forEach(moduleConfig => applyModuleConfig(router, injector, moduleConfig)); // Start monitoring the URL - if (!router.urlRouterProvider.interceptDeferred) { - router.urlRouter.listen(); - router.urlRouter.sync(); + if (!router.urlRouter.interceptDeferred) { + router.urlService.listen(); + router.urlService.sync(); } }); return router; -}; +} export function parentUIViewInjectFactory(r: StateRegistry) { return { fqn: null, context: r.root() } as ParentUIViewInject; } @@ -177,18 +174,20 @@ export function fnStateService(r: UIRouter) { return r.stateService; } export function fnTransitionService(r: UIRouter) { return r.transitionService; } export function fnUrlMatcherFactory(r: UIRouter) { return r.urlMatcherFactory; } export function fnUrlRouter(r: UIRouter) { return r.urlRouter; } +export function fnUrlService(r: UIRouter) { return r.urlService; } export function fnViewService(r: UIRouter) { return r.viewService; } export function fnStateRegistry(r: UIRouter) { return r.stateRegistry; } export function fnGlobals(r: any) { return r.globals; } export const _UIROUTER_SERVICE_PROVIDERS: Provider[] = [ - { provide: StateService, useFactory: fnStateService, deps: [UIRouter]}, - { provide: TransitionService, useFactory: fnTransitionService, deps: [UIRouter]}, - { provide: UrlMatcherFactory, useFactory: fnUrlMatcherFactory, deps: [UIRouter]}, - { provide: UrlRouter, useFactory: fnUrlRouter, deps: [UIRouter]}, - { provide: ViewService, useFactory: fnViewService, deps: [UIRouter]}, - { provide: StateRegistry, useFactory: fnStateRegistry, deps: [UIRouter]}, - { provide: Globals, useFactory: fnGlobals, deps: [UIRouter]}, + { provide: StateService, useFactory: fnStateService, deps: [UIRouter]}, + { provide: TransitionService, useFactory: fnTransitionService, deps: [UIRouter]}, + { provide: UrlMatcherFactory, useFactory: fnUrlMatcherFactory, deps: [UIRouter]}, + { provide: UrlRouter, useFactory: fnUrlRouter, deps: [UIRouter]}, + { provide: UrlService, useFactory: fnUrlService, deps: [UIRouter]}, + { provide: ViewService, useFactory: fnViewService, deps: [UIRouter]}, + { provide: StateRegistry, useFactory: fnStateRegistry, deps: [UIRouter]}, + { provide: Globals, useFactory: fnGlobals, deps: [UIRouter]}, ]; /**