Skip to content
This repository has been archived by the owner on Nov 27, 2023. It is now read-only.

Commit

Permalink
feat: add onReady lifecycle
Browse files Browse the repository at this point in the history
This notifies listeners on ready to be used components

Closes #1121
  • Loading branch information
KnisterPeter committed Oct 11, 2020
1 parent 2b541ee commit 7c4f592
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 15 deletions.
115 changes: 115 additions & 0 deletions lib/__tests__/on-ready-lifecycle-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// tslint:disable: no-implicit-dependencies
import { assert } from 'chai';
import { TSDI, component, initialize, inject } from '..';

describe('TSDI', () => {
let tsdi: TSDI;

beforeEach(() => {
tsdi = new TSDI();
});

afterEach(() => {
tsdi.close();
});

describe('with initializers', () => {
it('should notify when a component without initializer is ready to be used', (done) => {
@component
class Component {}
tsdi.register(Component);

tsdi.addLifecycleListener({
onReady(component: any): void {
assert.instanceOf(component, Component);
done();
},
});

tsdi.get(Component);
});

it('should notify when a component is ready to be used', (done) => {
@component
class Component {
@initialize
protected init(): void {
//
}
}
tsdi.register(Component);

tsdi.addLifecycleListener({
onReady(component: any): void {
assert.instanceOf(component, Component);
done();
},
});

tsdi.get(Component);
});

it('should notify when a component with async dependency is ready to be used', (done) => {
@component
class Dependency {
@initialize
protected async init(): Promise<void> {
return Promise.resolve();
}
}
tsdi.register(Dependency);

@component
class Component {
@inject
public readonly dependency!: Dependency;

@initialize
protected async init(): Promise<void> {
return Promise.resolve();
}
}
tsdi.register(Component);

tsdi.addLifecycleListener({
onReady(component: any): void {
if (component instanceof Component) {
assert.instanceOf(component, Component);
done();
}
},
});

tsdi.get(Component);
});

it('should notify when a component without initializer but with async dependency is ready to be used', (done) => {
@component
class Dependency {
@initialize
protected async init(): Promise<void> {
return Promise.resolve();
}
}
tsdi.register(Dependency);

@component
class Component {
@inject
public readonly dependency!: Dependency;
}
tsdi.register(Component);

tsdi.addLifecycleListener({
onReady(component: any): void {
if (component instanceof Component) {
assert.instanceOf(component, Component);
done();
}
},
});

tsdi.get(Component);
});
});
});
38 changes: 23 additions & 15 deletions lib/tsdi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export type Mock<T> = { -readonly [P in keyof T]: T[P] };

export interface LifecycleListener {
onCreate?(component: any): void;
onReady?(component: any): void;
onDestroy?(component: any): void;
}

Expand Down Expand Up @@ -151,25 +152,21 @@ export class TSDI {

public addLifecycleListener(lifecycleListener: LifecycleListener): void {
this.lifecycleListeners.push(lifecycleListener);
Object.keys(this.instances).forEach((idx) => {
this.notifyOnCreate(this.instances[parseInt(idx, 10)]);
});
Object.keys(this.instances).forEach((idx) =>
this.notifyOnCreate(this.instances[parseInt(idx, 10)])
);
}

private notifyOnCreate(component: any): void {
this.lifecycleListeners.forEach((l) => {
if (l.onCreate) {
l.onCreate(component);
}
});
this.lifecycleListeners.forEach((l) => l.onCreate?.(component));
}

private notifyOnReady(component: any): void {
this.lifecycleListeners.forEach((l) => l.onReady?.(component));
}

private notifyOnDestroy(component: any): void {
this.lifecycleListeners.forEach((l) => {
if (l.onDestroy) {
l.onDestroy(component);
}
});
this.lifecycleListeners.forEach((l) => l.onDestroy?.(component));
}

public addProperty(key: string, value: any): void {
Expand Down Expand Up @@ -537,13 +534,24 @@ export class TSDI {
if (awaiter) {
this.addInitializerPromise(
instance,
awaiter.then(() => instance[init].call(instance) || Promise.resolve())
awaiter
.then(() => instance[init].call(instance) || Promise.resolve())
.then(() => this.notifyOnReady(instance))
);
} else {
this.addInitializerPromise(instance, instance[init].call(instance));
this.addInitializerPromise(
instance,
(instance[init].call(instance) || Promise.resolve()).then(() =>
this.notifyOnReady(instance)
)
);
}
} else if (awaiter) {
// tslint:disable-next-line: no-floating-promises
awaiter.then(() => this.notifyOnReady(instance));
this.addInitializerPromise(instance, awaiter);
} else {
this.notifyOnReady(instance);
}
}

Expand Down

0 comments on commit 7c4f592

Please sign in to comment.