UNPKG

ngx-context

Version:

Angular Context: Easy property binding for router outlet and nested component trees.

323 lines (313 loc) 11 kB
import { Component, ChangeDetectionStrategy, ViewEncapsulation, ChangeDetectorRef, Optional, SkipSelf, Input, Directive, Host, TemplateRef, ViewContainerRef, NgModule } from '@angular/core'; import { Observable, Subject, ReplaySubject } from 'rxjs'; import { takeUntil, startWith, filter } from 'rxjs/operators'; import { __rest } from 'tslib'; function isSubscribableOrPromise(obj) { return obj instanceof Observable || obj instanceof Subject || obj instanceof Promise; } function parseKeys(input) { return (Array.isArray(input) ? input : input.split(/\s+/)).filter(key => key); } class ContextProviderComponent { constructor(source) { this.source = source; this.initialized = false; this._contextMap = {}; this._provide = ''; this.provided = new Map(); this.change$ = new ReplaySubject(1); this.reset$ = new Subject(); } set contextMap(map) { this._contextMap = map || {}; } get contextMap() { return this._contextMap; } set provide(value) { this._provide = value || ''; } get provide() { return this._provide; } get component() { var _a; return ((_a = this.source['_view']) === null || _a === void 0 ? void 0 : _a.component) || this.source['context']; } init() { setTimeout(() => { const THIS = this; const context = new Map(); const provided = parseKeys(this.provide).filter(key => key && key in this.component); provided.forEach(key => { if (isSubscribableOrPromise(this.component[key])) { this.change$.next(key); return; } const propertyDescriptor = Object.getOwnPropertyDescriptor(this.component, key) || Object.getOwnPropertyDescriptor(this.component.__proto__, key); this.provided.set(key, propertyDescriptor); const { value, writable, get: getter, set: setter } = propertyDescriptor, prop = __rest(propertyDescriptor, ["value", "writable", "get", "set"]); Object.defineProperty(this.component, key, Object.assign(Object.assign({}, prop), { get: getter ? function () { return getter.call(this); } : function () { return context.get(key); }, set: setter ? function () { setter.apply(this, arguments); THIS.change$.next(key); } : function (newValue) { context.set(key, newValue); THIS.change$.next(key); } })); this.component[key] = value || this.component[key]; }); }, 0); } reset() { this.provided.forEach((propertyDescriptor, key) => { const value = this.component[key]; Object.defineProperty(this.component, key, propertyDescriptor); this.component[key] = value; }); this.provided.clear(); this.change$.next(''); this.reset$.next(); } ngOnChanges() { if (this.initialized) { this.reset(); if (this.source) this.init(); } } ngOnInit() { this.initialized = true; this.ngOnChanges(); } } ContextProviderComponent.decorators = [ { type: Component, args: [{ selector: 'context-provider', template: '<ng-content></ng-content>', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None },] } ]; ContextProviderComponent.ctorParameters = () => [ { type: ChangeDetectorRef, decorators: [{ type: Optional }, { type: SkipSelf }] } ]; ContextProviderComponent.propDecorators = { contextMap: [{ type: Input }], provide: [{ type: Input }] }; class AbstractContextConsumer { constructor(provider, target) { this.provider = provider; this.target = target; this.destroy$ = new Subject(); this._contextMap = {}; this._consume = ''; this.consumed = new Map(); } set contextMap(map) { this._contextMap = map || {}; } get contextMap() { return this._contextMap; } set consume(consume) { this._consume = consume || ''; } get consume() { return this._consume; } get component() { var _a; return ((_a = this.target['_view']) === null || _a === void 0 ? void 0 : _a.component) || this.target['context']; } init() { const consumed = parseKeys(this.consume); this.provider.reset$ .pipe(takeUntil(this.destroy$)) .subscribe(() => this.ngOnChanges()); if (this.provider.provide.length) this.provider.change$ .pipe(takeUntil(this.destroy$), startWith(...Array.from(this.provider.provided.keys())), filter(key => !!key)) .subscribe(providerKey => this.syncProperties(consumed, providerKey)); } reset() { this.consumed.forEach((value, key) => { this.component[key] = value; }); this.consumed.clear(); } syncProperties(consumed, providerKey) { let key = this.provider.contextMap[providerKey] || providerKey; key = this.contextMap[key] || key; if (consumed.length && consumed.indexOf(key) < 0) return; if (!this.consumed.has(key)) this.consumed.set(key, this.component[key]); this.component[key] = this.provider.component[providerKey]; this.target.markForCheck(); } ngOnChanges() { if (this.initialized) { this.destroy$.next(); this.reset(); if (this.target && this.provider) this.init(); } } ngOnDestroy() { this.destroy$.next(); } ngOnInit() { this.initialized = true; this.ngOnChanges(); } } AbstractContextConsumer.decorators = [ { type: Directive } ]; AbstractContextConsumer.ctorParameters = () => [ { type: ContextProviderComponent }, { type: ChangeDetectorRef } ]; AbstractContextConsumer.propDecorators = { contextMap: [{ type: Input }], consume: [{ type: Input }] }; class ContextConsumerComponent extends AbstractContextConsumer { constructor(providerComponent, parent) { super(providerComponent, parent); } } ContextConsumerComponent.decorators = [ { type: Component, args: [{ selector: 'context-consumer', template: '', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None },] } ]; ContextConsumerComponent.ctorParameters = () => [ { type: ContextProviderComponent, decorators: [{ type: Optional }, { type: SkipSelf }] }, { type: ChangeDetectorRef, decorators: [{ type: Optional }, { type: SkipSelf }] } ]; class ContextConsumerDirective extends AbstractContextConsumer { constructor(providerComponent, host) { super(providerComponent, host); } set consume(consume) { this._consume = consume || ''; } get consume() { return this._consume; } } ContextConsumerDirective.decorators = [ { type: Directive, args: [{ selector: '[contextConsumer]', },] } ]; ContextConsumerDirective.ctorParameters = () => [ { type: ContextProviderComponent, decorators: [{ type: Optional }, { type: SkipSelf }] }, { type: ChangeDetectorRef, decorators: [{ type: Optional }, { type: Host }] } ]; ContextConsumerDirective.propDecorators = { consume: [{ type: Input, args: ['contextConsumer',] }] }; class Context { constructor() { this.$implicit = {}; } } class ContextDisposerDirective { constructor(tempRef, vcRef, provider) { this.tempRef = tempRef; this.vcRef = vcRef; this.provider = provider; this.destroy$ = new Subject(); this._dispose = ''; } set dispose(dispose) { this._dispose = dispose || ''; } get dispose() { return this._dispose; } init() { const disposed = parseKeys(this.dispose); this.provider.reset$ .pipe(takeUntil(this.destroy$)) .subscribe(() => this.ngOnChanges()); if (this.provider.provide.length) this.provider.change$ .pipe(takeUntil(this.destroy$), startWith(...Array.from(this.provider.provided.keys())), filter(key => !!key)) .subscribe(providerKey => this.syncProperties(disposed, providerKey)); } reset() { this.view = this.vcRef.createEmbeddedView(this.tempRef, new Context()); } syncProperties(disposed, providerKey) { const key = this.provider.contextMap[providerKey] || providerKey; if (disposed.length && disposed.indexOf(key) < 0) return; const value = this.provider.component[providerKey]; this.view.context.$implicit[key] = value; this.view.context[key] = value; this.view.markForCheck(); } ngOnChanges() { this.ngOnDestroy(); this.reset(); if (this.provider && this.tempRef && this.vcRef) this.init(); } ngOnDestroy() { this.destroy$.next(); if (this.view) this.vcRef.clear(); } } ContextDisposerDirective.decorators = [ { type: Directive, args: [{ selector: '[contextDisposer]', },] } ]; ContextDisposerDirective.ctorParameters = () => [ { type: TemplateRef, decorators: [{ type: Optional }] }, { type: ViewContainerRef, decorators: [{ type: Optional }] }, { type: ContextProviderComponent, decorators: [{ type: Optional }, { type: SkipSelf }] } ]; ContextDisposerDirective.propDecorators = { dispose: [{ type: Input, args: ['contextDisposer',] }] }; class NgxContextModule { } NgxContextModule.decorators = [ { type: NgModule, args: [{ declarations: [ ContextConsumerComponent, ContextConsumerDirective, ContextDisposerDirective, ContextProviderComponent, ], exports: [ ContextConsumerComponent, ContextConsumerDirective, ContextDisposerDirective, ContextProviderComponent, ], },] } ]; /** * Generated bundle index. Do not edit. */ export { ContextConsumerComponent, ContextConsumerDirective, ContextDisposerDirective, ContextProviderComponent, NgxContextModule, AbstractContextConsumer as ɵa }; //# sourceMappingURL=ngx-context.js.map