ngx-context
Version:
Angular Context: Easy property binding for router outlet and nested component trees.
323 lines (313 loc) • 11 kB
JavaScript
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