diff --git a/src/globals.ts b/src/globals.ts index f132a93a5..8a12ae064 100644 --- a/src/globals.ts +++ b/src/globals.ts @@ -63,7 +63,7 @@ export class UIRouterGlobals { const clearCurrentTransition = () => { if (this.transition === $transition$) this.transition = null; }; - $transition$.promise.finally(clearCurrentTransition) + $transition$.promise.then(clearCurrentTransition, clearCurrentTransition); }; diff --git a/src/ng2/directives.ts b/src/ng2/directives.ts index d3d7c17dd..83ebef4c2 100644 --- a/src/ng2/directives.ts +++ b/src/ng2/directives.ts @@ -1,9 +1,11 @@ - import {UiSref} from "../ng2/uiSref"; -import {UiSrefClass} from "../ng2/uiSrefActive"; +import {UiSrefActive} from "../ng2/uiSrefActive"; import {UiView} from "../ng2/uiView"; +import {UiSrefStatus} from "./uiSrefStatus"; + +export * from "./uiView"; export * from "./uiSref"; +export * from "./uiSrefStatus"; export * from "./uiSrefActive"; -export * from "./uiView"; -export let UIROUTER_DIRECTIVES = [UiSref, UiSrefClass, UiView]; +export let UIROUTER_DIRECTIVES = [UiSref, UiView, UiSrefActive, UiSrefStatus]; diff --git a/src/ng2/providers.ts b/src/ng2/providers.ts index 07492963f..d75941280 100644 --- a/src/ng2/providers.ts +++ b/src/ng2/providers.ts @@ -11,6 +11,7 @@ import {UiView} from "./uiView"; import {ng2ViewsBuilder, Ng2ViewConfig} from "./viewsBuilder"; import {Ng2ViewDeclaration} from "./interface"; import {UIRouterConfig} from "./uiRouterConfig"; +import {UIRouterGlobals} from "../globals"; let uiRouterFactory = (routerConfig: UIRouterConfig) => { let router = new UIRouter(); @@ -46,6 +47,8 @@ export const UIROUTER_PROVIDERS: Provider[] = [ provide(StateRegistry, { useFactory: (r: UIRouter) => { return r.stateRegistry; }, deps: [UIRouter]}), + provide(UIRouterGlobals, { useFactory: (r: UIRouter) => { return r.globals; }, deps: [UIRouter]}), + provide(UiView.INJECT.context, { useFactory: (r: StateRegistry) => { console.log(r); return r.root(); }, deps: [StateRegistry]} ), provide(UiView.INJECT.fqn, { useValue: null }) diff --git a/src/ng2/uiSref.ts b/src/ng2/uiSref.ts index b872f03f1..07542980d 100644 --- a/src/ng2/uiSref.ts +++ b/src/ng2/uiSref.ts @@ -29,7 +29,6 @@ export class UiSref { @Optional() private _anchorUiSref: AnchorUiSref ) { } - set "ui-sref"(val) { this.state = val; this.update(); } set "uiSref"(val) { this.state = val; this.update(); } set "uiParams"(val) { this.params = val; this.update(); } set "uiOptions"(val) { this.options = val; this.update(); } @@ -42,7 +41,6 @@ export class UiSref { if (this._anchorUiSref) { this._anchorUiSref.update(this._router.stateService.href(this.state, this.params)); } - // TODO: process ui-sref-active } go() { diff --git a/src/ng2/uiSrefActive.ts b/src/ng2/uiSrefActive.ts index 6967c64cf..baecc119a 100644 --- a/src/ng2/uiSrefActive.ts +++ b/src/ng2/uiSrefActive.ts @@ -1,52 +1,20 @@ -/** @module ng2 */ /** */ -import {UIRouter} from "../router"; -import {Directive} from "angular2/core"; -import {UiSref} from "./uiSref"; -@Directive({ - selector: '[uiSrefClass]', - inputs: ['uiSrefClass'] -}) -export class UiSrefClass { - // current statuses of the bound uiSref directive - active = false; - exact = false; - entering = false; - exiting = false; - inactive = true; +import {Directive, Input, ElementRef, Host, Renderer} from "angular2/core"; +import {UiSrefStatus, SrefStatus} from "./uiSrefStatus"; - patterns: any; - classes: string; - sref: UiSref; +@Directive({ selector: '[uiSrefActive],[uiSrefActiveEq]' }) +export class UiSrefActive { + private _classes: string[] = []; + @Input('uiSrefActive') set active(val) { this._classes = val.split("\s+")}; - //constructor($transitions: TransitionService, public router: UIRouter) { - constructor(public router: UIRouter) { - this.ngOnDestroy = router.transitionService.onSuccess({}, this._update.bind(this)); - } - - ngOnDestroy() {} - - /** - * e.g. - * { - * active: 'active && !exiting', - * loading: 'entering', - * active: matches('admin.*') - * } - */ - set uiSrefClass(val) { - console.log(val); // [uiSrefClass]="{active: isActive}" logs as "{active: undefined}" - this.patterns = val; - } - - public provideUiSref(sref: UiSref) { - this.sref = sref; - this._update(); - } + private _classesEq: string[] = []; + @Input('uiSrefActiveEq') set activeEq(val) { this._classesEq = val.split("\s+")}; - private _update() { - if (!this.sref) return; - // update classes + constructor(uiSrefStatus: UiSrefStatus, rnd: Renderer, @Host() host: ElementRef) { + uiSrefStatus.uiSrefStatus.subscribe((next: SrefStatus) => { + this._classes.forEach(cls => rnd.setElementClass(host.nativeElement, cls, next.active)); + this._classesEq.forEach(cls => rnd.setElementClass(host.nativeElement, cls, next.exact)); + }); } } diff --git a/src/ng2/uiSrefStatus.ts b/src/ng2/uiSrefStatus.ts new file mode 100644 index 000000000..2f2b3d92f --- /dev/null +++ b/src/ng2/uiSrefStatus.ts @@ -0,0 +1,101 @@ +import {Directive, Output, EventEmitter} from "angular2/core"; +import {StateService} from "../state/stateService"; +import {UiSref} from "./uiSref"; +import {UIRouter} from "../router"; +import {Node} from "../path/node"; +import {TransitionService} from "../transition/transitionService"; +import {Transition} from "../transition/transition"; +import {TargetState} from "../state/targetState"; +import {TreeChanges} from "../transition/interface"; +import {State} from "../state/stateObject"; +import {anyTrueR, tail} from "../common/common"; +import {UIRouterGlobals} from "../globals"; + +export interface SrefStatus { + active: boolean; + exact: boolean; + entering: boolean; + exiting: boolean; +} + +/** + * Emits events when the uiSref status changes + * + * This API is subject to change. + */ +@Directive({ selector: '[uiSrefStatus],[uiSrefActive],[uiSrefActiveEq]' }) +export class UiSrefStatus { + private _deregisterHook; + + // current statuses of the state/params the uiSref directive is linking to + @Output("uiSrefStatus") uiSrefStatus = new EventEmitter(); + + status: SrefStatus = { + active: false, + exact: false, + entering: false, + exiting: false + }; + + constructor(transitionService: TransitionService, + private _globals: UIRouterGlobals, + private _stateService: StateService, + public sref: UiSref) { + this._deregisterHook = transitionService.onStart({}, ($transition$) => this._transition($transition$)); + } + + ngOnInit() { + let lastTrans = this._globals.transitionHistory.peekTail(); + if (lastTrans != null) { + this._transition(lastTrans); + } + } + + ngOnDestroy() { + this._deregisterHook() + } + + private _setStatus(status: SrefStatus) { + this.status = status; + this.uiSrefStatus.emit(status); + } + + private _transition($transition$: Transition) { + let sref = this.sref; + + let status: SrefStatus = { + active: false, + exact: false, + entering: false, + exiting: false + }; + + let srefTarget: TargetState = this._stateService.target(sref.state, sref.params, sref.options); + if (!srefTarget.exists()) { + return this._setStatus(status); + } + + let tc: TreeChanges = $transition$.treeChanges(); + let state: State = srefTarget.$state(); + const isTarget = (node: Node) => node.state === state; + + status.active = tc.from.map(isTarget).reduce(anyTrueR, false); + status.exact = tail(tc.from.map(isTarget)) === true; + status.entering = tc.entering.map(isTarget).reduce(anyTrueR, false); + status.exiting = tc.exiting.map(isTarget).reduce(anyTrueR, false); + + if ($transition$.isActive()) { + this._setStatus(status); + } + + let update = (currentPath: Node[]) => () => { + if (!$transition$.isActive()) return; // superseded + status.active = currentPath.map(isTarget).reduce(anyTrueR, false); + status.exact = tail(currentPath.map(isTarget)) === true; + status.entering = status.exiting = false; + this._setStatus(status); + }; + + $transition$.promise.then(update(tc.to), update(tc.from)); + } +} diff --git a/src/ng2/uiView.ts b/src/ng2/uiView.ts index 913ea2719..fd7bc6443 100755 --- a/src/ng2/uiView.ts +++ b/src/ng2/uiView.ts @@ -30,16 +30,17 @@ const getProviders = (injector) => { color: grey; }` ], - template: ` -
- -
-
ui-view #{{uiViewData.id}} created by '{{ parentContext.name || "(root)" }}' state
-
name: (absolute) '{{uiViewData.fqn}}' (contextual) '{{uiViewData.name}}@{{parentContext.name}}'
-
currently filled by: '{{(uiViewData.config && uiViewData.config.context) || 'empty...'}}'
-
- -
` + template: `
`, + // debugtemplate: ` + //
+ // + //
+ //
ui-view #{{uiViewData.id}} created by '{{ parentContext.name || "(root)" }}' state
+ //
name: (absolute) '{{uiViewData.fqn}}' (contextual) '{{uiViewData.name}}@{{parentContext.name}}'
+ //
currently filled by: '{{(uiViewData.config && uiViewData.config.context) || 'empty...'}}'
+ //
+ // + //
` }) export class UiView { @Input() name: string;