Skip to content

Commit 3d532b6

Browse files
feat(directives): Export directives using exportAs: for use as template variables
Closes #149
1 parent 8f16179 commit 3d532b6

File tree

3 files changed

+54
-66
lines changed

3 files changed

+54
-66
lines changed

src/directives/uiSref.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ export class AnchorUISref {
6868
*/
6969
@Directive({
7070
selector: '[uiSref]',
71+
exportAs: 'uiSref',
7172
host: { '(click)': 'go()' }
7273
})
7374
export class UISref implements OnChanges {
@@ -109,7 +110,7 @@ export class UISref implements OnChanges {
109110
/** @internalapi */ private _statesSub: Subscription;
110111
/** @internalapi */ private _router: UIRouter;
111112
/** @internalapi */ private _anchorUISref: AnchorUISref;
112-
/** @internalapi */ public parent: ParentUIViewInject;
113+
/** @internalapi */ private _parent: ParentUIViewInject;
113114

114115
constructor(
115116
_router: UIRouter,
@@ -118,7 +119,7 @@ export class UISref implements OnChanges {
118119
) {
119120
this._router = _router;
120121
this._anchorUISref = _anchorUISref;
121-
this.parent = parent;
122+
this._parent = parent;
122123

123124
this._statesSub = _router.globals.states$.subscribe(() => this.update());
124125
}
@@ -145,7 +146,7 @@ export class UISref implements OnChanges {
145146
this.targetState$.unsubscribe();
146147
}
147148

148-
update() {
149+
private update() {
149150
let $state = this._router.stateService;
150151
if (this._emit) {
151152
let newTarget = $state.target(this.state, this.params, this.getOptions());
@@ -160,7 +161,7 @@ export class UISref implements OnChanges {
160161

161162
getOptions() {
162163
let defaultOpts: TransitionOptions = {
163-
relative: this.parent && this.parent.context && this.parent.context.name,
164+
relative: this._parent && this._parent.context && this._parent.context.name,
164165
inherit: true ,
165166
source: "sref"
166167
};

src/directives/uiSrefStatus.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,12 +183,15 @@ function mergeSrefStatus(left: SrefStatus, right: SrefStatus): SrefStatus {
183183
*
184184
* This API is subject to change.
185185
*/
186-
@Directive({ selector: '[uiSrefStatus],[uiSrefActive],[uiSrefActiveEq]' })
186+
@Directive({
187+
selector: '[uiSrefStatus],[uiSrefActive],[uiSrefActiveEq]',
188+
exportAs: 'uiSrefStatus'
189+
})
187190
export class UISrefStatus {
188191
/** current statuses of the state/params the uiSref directive is linking to */
189192
@Output("uiSrefStatus") uiSrefStatus = new EventEmitter<SrefStatus>(false);
190193
/** Monitor all child components for UISref(s) */
191-
@ContentChildren(UISref, {descendants: true}) srefs: QueryList<UISref>;
194+
@ContentChildren(UISref, {descendants: true}) private _srefs: QueryList<UISref>;
192195

193196
/** The current status */
194197
status: SrefStatus;
@@ -218,8 +221,8 @@ export class UISrefStatus {
218221
// Watch the @ContentChildren UISref[] components and get their target states
219222

220223
// let srefs$: Observable<UISref[]> = of(this.srefs.toArray()).concat(this.srefs.changes);
221-
this._srefs$ = new BehaviorSubject(this.srefs.toArray());
222-
this._srefChangesSub = this.srefs.changes.subscribe(srefs => this._srefs$.next(srefs));
224+
this._srefs$ = new BehaviorSubject(this._srefs.toArray());
225+
this._srefChangesSub = this._srefs.changes.subscribe(srefs => this._srefs$.next(srefs));
223226

224227
let targetStates$: Observable<TargetState[]> =
225228
switchMap.call(this._srefs$, (srefs: UISref[]) =>

src/directives/uiView.ts

Lines changed: 42 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -87,40 +87,25 @@ const ng2ComponentInputs = (factory: ComponentFactory<any>): InputMapping[] => {
8787
*/
8888
@Component({
8989
selector: 'ui-view, [ui-view]',
90+
exportAs: 'uiView',
9091
template: `
9192
<ng-template #componentTarget></ng-template>
92-
<ng-content *ngIf="!componentRef"></ng-content>
93+
<ng-content *ngIf="!_componentRef"></ng-content>
9394
`
94-
// styles: [`
95-
// .done-true {
96-
// text-decoration: line-through;
97-
// color: grey;
98-
// }`
99-
// ],
100-
// template: `
101-
// <div style="padding: 1em; border: 1px solid lightgrey;">
102-
//
103-
// <div #content style="color: lightgrey; font-size: smaller;">
104-
// <div>ui-view #{{uiViewData?.id}} created by '{{ parentContext?.name || "(root)" }}' state</div>
105-
// <div>name: (absolute) '{{uiViewData?.fqn}}' (contextual) '{{uiViewData?.name}}@{{parentContext?.name}}' </div>
106-
// <div>currently filled by: '{{(uiViewData?.config && uiViewData?.config?.viewDecl?.$context) || 'empty...'}}'</div>
107-
// </div>
108-
//
109-
// </div>`
11095
})
11196
export class UIView {
112-
@ViewChild('componentTarget', {read: ViewContainerRef}) componentTarget: ViewContainerRef;
97+
@ViewChild('componentTarget', {read: ViewContainerRef}) _componentTarget: ViewContainerRef;
11398
@Input('name') name: string;
11499
@Input('ui-view') set _name(val: string) { this.name = val; }
115100
/** The reference to the component currently inside the viewport */
116-
componentRef: ComponentRef<any>;
101+
_componentRef: ComponentRef<any>;
117102
/** Deregisters the ui-view from the view service */
118-
deregisterUIView: Function;
103+
private _deregisterUIView: Function;
119104
/** Deregisters the master uiCanExit transition hook */
120-
deregisterHook: Function;
105+
private _deregisterHook: Function;
121106
/** Data about the this UIView */
122-
uiViewData: ActiveUIView = <any> {};
123-
parent: ParentUIViewInject;
107+
private _uiViewData: ActiveUIView = <any> {};
108+
private _parent: ParentUIViewInject;
124109

125110
static PARENT_INJECT = "UIView.PARENT_INJECT";
126111

@@ -129,26 +114,26 @@ export class UIView {
129114
@Inject(UIView.PARENT_INJECT) parent,
130115
public viewContainerRef: ViewContainerRef,
131116
) {
132-
this.parent = parent;
117+
this._parent = parent;
133118
}
134119

135120
ngOnInit() {
136121
const router = this.router;
137-
const parentFqn = this.parent.fqn;
122+
const parentFqn = this._parent.fqn;
138123
const name = this.name || '$default';
139124

140-
this.uiViewData = {
125+
this._uiViewData = {
141126
$type: 'ng2',
142127
id: id++,
143128
name: name,
144129
fqn: parentFqn ? parentFqn + "." + name : name,
145-
creationContext: this.parent.context,
146-
configUpdated: this.viewConfigUpdated.bind(this),
130+
creationContext: this._parent.context,
131+
configUpdated: this._viewConfigUpdated.bind(this),
147132
config: undefined
148133
};
149134

150-
this.deregisterHook = router.transitionService.onBefore({}, trans => this.applyUiCanExitHook(trans));
151-
this.deregisterUIView = router.viewService.registerUIView(this.uiViewData);
135+
this._deregisterHook = router.transitionService.onBefore({}, trans => this._applyUiCanExitHook(trans));
136+
this._deregisterUIView = router.viewService.registerUIView(this._uiViewData);
152137
}
153138

154139
/**
@@ -159,12 +144,12 @@ export class UIView {
159144
*
160145
* If both are true, adds the uiCanExit component function as a hook to that singular Transition.
161146
*/
162-
applyUiCanExitHook(trans: Transition) {
163-
const instance = this.componentRef && this.componentRef.instance;
147+
private _applyUiCanExitHook(trans: Transition) {
148+
const instance = this._componentRef && this._componentRef.instance;
164149
const uiCanExitFn: TransitionHookFn = instance && instance.uiCanExit;
165150

166151
if (isFunction(uiCanExitFn)) {
167-
const state: StateDeclaration = parse("uiViewData.config.viewDecl.$context.self")(this);
152+
const state: StateDeclaration = parse("_uiViewData.config.viewDecl.$context.self")(this);
168153

169154
if (trans.exiting().indexOf(state) !== -1) {
170155
trans.onStart({}, function() {
@@ -174,55 +159,58 @@ export class UIView {
174159
}
175160
}
176161

177-
disposeLast() {
178-
if (this.componentRef) this.componentRef.destroy();
179-
this.componentRef = null;
162+
private _disposeLast() {
163+
if (this._componentRef) this._componentRef.destroy();
164+
this._componentRef = null;
180165
}
181166

182167
ngOnDestroy() {
183-
if (this.deregisterUIView) this.deregisterUIView();
184-
if (this.deregisterHook) this.deregisterHook();
185-
this.disposeLast();
168+
if (this._deregisterUIView) this._deregisterUIView();
169+
if (this._deregisterHook) this._deregisterHook();
170+
this._disposeLast();
186171
}
187172

188173
/**
189174
* The view service is informing us of an updated ViewConfig
190175
* (usually because a transition activated some state and its views)
191176
*/
192-
viewConfigUpdated(config: ViewConfig) {
177+
_viewConfigUpdated(config: ViewConfig) {
193178
// The config may be undefined if there is nothing currently targeting this UIView.
194179
// Dispose the current component, if there is one
195-
if (!config) return this.disposeLast();
180+
if (!config) return this._disposeLast();
196181

197182
// Only care about Ng2 configs
198183
if (!(config instanceof Ng2ViewConfig)) return;
199184

200185
// The "new" viewconfig is already applied, so exit early
201-
if (this.uiViewData.config === config) return;
186+
if (this._uiViewData.config === config) return;
202187

203188
// This is a new ViewConfig. Dispose the previous component
204-
this.disposeLast();
205-
trace.traceUIViewConfigUpdated(this.uiViewData, config && config.viewDecl.$context);
189+
this._disposeLast();
190+
trace.traceUIViewConfigUpdated(this._uiViewData, config && config.viewDecl.$context);
206191

207-
this.applyUpdatedConfig(config);
192+
this._applyUpdatedConfig(config);
208193
}
209194

210-
applyUpdatedConfig(config: Ng2ViewConfig) {
211-
this.uiViewData.config = config;
195+
private _applyUpdatedConfig(config: Ng2ViewConfig) {
196+
this._uiViewData.config = config;
212197
// Create the Injector for the routed component
213198
let context = new ResolveContext(config.path);
214-
let componentInjector = this.getComponentInjector(context);
199+
let componentInjector = this._getComponentInjector(context);
215200

216201
// Get the component class from the view declaration. TODO: allow promises?
217202
let componentClass = config.viewDecl.component;
218203

219204
// Create the component
220205
let compFactoryResolver = componentInjector.get(ComponentFactoryResolver);
221206
let compFactory = compFactoryResolver.resolveComponentFactory(componentClass);
222-
this.componentRef = this.componentTarget.createComponent(compFactory, undefined, componentInjector);
207+
this._componentRef = this._componentTarget.createComponent(compFactory, undefined, componentInjector);
223208

224209
// Wire resolves to @Input()s
225-
this.applyInputBindings(compFactory, this.componentRef, context, componentClass);
210+
this._applyInputBindings(compFactory, this._componentRef.instance, context, componentClass);
211+
212+
// Initiate change detection for the newly created component
213+
this._componentRef.changeDetectorRef.markForCheck();
226214
}
227215

228216
/**
@@ -235,12 +223,12 @@ export class UIView {
235223
*
236224
* @returns an Injector
237225
*/
238-
getComponentInjector(context: ResolveContext): Injector {
226+
private _getComponentInjector(context: ResolveContext): Injector {
239227
// Map resolves to "useValue: providers"
240228
let resolvables = context.getTokens().map(token => context.getResolvable(token)).filter(r => r.resolved);
241229
let newProviders = resolvables.map(r => ({ provide: r.token, useValue: r.data }));
242230

243-
let parentInject = { context: this.uiViewData.config.viewDecl.$context, fqn: this.uiViewData.fqn };
231+
let parentInject = { context: this._uiViewData.config.viewDecl.$context, fqn: this._uiViewData.fqn };
244232
newProviders.push({ provide: UIView.PARENT_INJECT, useValue: parentInject });
245233

246234
let parentComponentInjector = this.viewContainerRef.injector;
@@ -256,9 +244,8 @@ export class UIView {
256244
* Finds component inputs which match resolves (by name) and sets the input value
257245
* to the resolve data.
258246
*/
259-
applyInputBindings(factory: ComponentFactory<any>, ref: ComponentRef<any>, context: ResolveContext, componentClass) {
260-
const component = ref.instance;
261-
const bindings = this.uiViewData.config.viewDecl['bindings'] || {};
247+
private _applyInputBindings(factory: ComponentFactory<any>, component: any, context: ResolveContext, componentClass) {
248+
const bindings = this._uiViewData.config.viewDecl['bindings'] || {};
262249
const explicitBoundProps = Object.keys(bindings);
263250

264251
// Returns the actual component property for a renamed an input renamed using `@Input('foo') _foo`.
@@ -285,8 +272,5 @@ export class UIView {
285272
.map(addResolvable)
286273
.filter(tuple => tuple.resolvable && tuple.resolvable.resolved)
287274
.forEach(tuple => { component[tuple.prop] = tuple.resolvable.data; });
288-
289-
// Initiate change detection for the newly created component
290-
ref.changeDetectorRef.detectChanges();
291275
}
292276
}

0 commit comments

Comments
 (0)