From 0a89cc1cfe41d34fd0671785d00b0cd117957691 Mon Sep 17 00:00:00 2001 From: Rafael Bardini Date: Fri, 13 Jul 2018 11:17:12 +0200 Subject: [PATCH] frint: Expose class methods on the app instance level (#431) * Expose class methods on the app instance level * Update documentation * Fix linting errors * Don't overwrite app fields with options methods * Remove `app.getMethods` method * Rename App index signature --- packages/frint/README.md | 1 + packages/frint/src/App.spec.ts | 29 ++++++++++++++++++++++++++++- packages/frint/src/App.ts | 20 ++++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/packages/frint/README.md b/packages/frint/README.md index 2bd4bea8..c1c95454 100644 --- a/packages/frint/README.md +++ b/packages/frint/README.md @@ -275,6 +275,7 @@ The base App class. 1. `options` (`Object`) * `options.name`: (`String` [required]): Name of your App. + * `options.methods`: (`Object` [optional]): Object with the methods your App exposes on the instance level. * `options.initialize`: (`Function` [optional]): Called when App is constructed. * `options.beforeDestroy`: (`Function` [optional]): Called when App is about to be destroyed. * `options.providers`: (`Array` [optional]): Array of provider objects. diff --git a/packages/frint/src/App.spec.ts b/packages/frint/src/App.spec.ts index 85053930..e8f08168 100644 --- a/packages/frint/src/App.spec.ts +++ b/packages/frint/src/App.spec.ts @@ -13,7 +13,7 @@ describe('frint › App', () => { }).to.throw(/Must provide `name` in options/); }); - it('gets option value', () => { + it('gets name option value', () => { const app = new App({ name: 'MyApp', }); @@ -21,6 +21,33 @@ describe('frint › App', () => { expect(app.getName()).to.equal('MyApp'); }); + it('exposes methods as class properties', () => { + const methods = { + foo() { return 'foo'; }, + }; + + const app = new App({ + name: 'MyApp', + methods, + }); + + expect(app.foo()).to.equal('foo'); + }); + + it('does not overwrite app properties or methods with options methods', () => { + const methods = { + // tslint:disable-next-line:no-empty + getName() {}, + }; + + expect(() => ( + new App({ + name: 'MyApp', + methods, + }) + )).to.throw(/Cannot overwrite app's `getName` property or method with options method/); + }); + it('gets parent and root app', () => { const rootApp = new App({ name: 'RootApp', diff --git a/packages/frint/src/App.ts b/packages/frint/src/App.ts index e327f030..21813ec1 100644 --- a/packages/frint/src/App.ts +++ b/packages/frint/src/App.ts @@ -32,6 +32,10 @@ function makeInstanceKey(region = null, regionKey = null, multi = false) { return key; } +export interface Methods { + [key: string]: () => any; +} + export interface ProviderNames { component: string; container: string; @@ -75,6 +79,7 @@ export interface AppRegistration { export interface AppOptions { name?: string; + methods?: Methods; parentApp?: App; providers?: FrintProvider[]; providerNames?: ProviderNames; @@ -88,6 +93,7 @@ export interface AppClass { } export class App { + [method: string]: any; public container: Container; private options: AppOptions; private _appsCollection: AppRegistration[]; @@ -96,6 +102,7 @@ export class App { constructor(opts: AppOptions) { this.options = { name: null, + methods: {}, parentApp: null, providers: [], @@ -116,6 +123,19 @@ export class App { throw new Error('Must provide `name` in options'); } + // expose methods as class properties + Object.keys(this.options.methods).forEach(methodName => { + const method = this.options.methods[methodName]; + + if (typeof method === 'function') { + if (this[methodName] !== undefined) { + throw new Error(`Cannot overwrite app's \`${methodName}\` property or method with options method.`); + } + + this[methodName] = method.bind(this); + } + }); + // children - create Observable if root this._appsCollection = []; this._apps$ = new BehaviorSubject(this._appsCollection);