コンポーネントのAngular2のような登録とknockoutjsの依存関係

こんにちは



Angular2のコンポーネントの属性登録が好きで、knockoutjsを使用したプロジェクトで同様のことをしたかったのです。



@Component({ selector: "setup-add-edit-street-name", template: require("text!./AddEditStreetName.tmpl.html"), directives: [BeatSelector] }) export class AddEditStreetNameComponent extends AddEditModalBaseComponent<StreetNameViewModel> { constructor(@Inject("params") params, streetNameService: StreetNameService) { super(params, streetNameService); } location = ko.observable() }
      
      





ノックアウトのコンポーネントはかなり前に登場しました。 ただし、組み込みの依存関係注入サポートの欠如、および個別のコンポーネント登録の必要性は、やや面倒でした。



依存性注入



この記事では、コンポーネントローダーとそれらを使用してDIサポートを追加する方法については説明しません。 最後に、 このパッケージを変更したとしか言えません。



使用法:

 //   kontainer.registerFactory('taskService', ['http', 'fileUploadService', (http, fileUploadService) => new TaskService(http, fileUploadService)); //    ko.components.register('task-component', { viewModel: ['params', 'taskService', (service) => new TaskComponent(params, service) ], template: '<p data-bind="text: name"></p>' });
      
      





paramsは、マークアップを介してコンポーネントに渡されたパラメーターです。



ここでの問題は、コンポーネントの登録があまり便利ではないことです。 簡単に封印でき、サービスの登録を忘れやすい。 また、依存関係をより明確にしたかったのです。



解決策



このアイデアを実装するには、typescriptでデコレータがどのように機能するかを理解する必要があります。 要するに、ある時点で呼び出されるのは単なる関数またはファクトリです(ドキュメントで見つけることができます)。



コンポーネント登録デコレーター:

 export interface ComponentParams { selector: string; template?: string; templateUrl?: string; directives?: Function[]; } export function Component(options: ComponentParams) { return (target: { new (...args) }) => { if (!ko.components.isRegistered(options.selector)) { if (!options.template && !options.templateUrl) { throw Error(`Component ${target.name} must have template`); } const factory = getFactory(target); const config = { template: options.template || { require: options.templateUrl }, viewModel: factory }; ko.components.register(options.selector, config); } }; }
      
      





ご覧のとおり、デコレータは特別なことは何もしません。 getFactory関数のすべての魔法

 interface InjectParam { index: number; dependency: string; } const injectMetadataKey = Symbol("inject"); function getFactory(target: { new (...args) }) { const deps = Reflect.getMetadata("design:paramtypes", target).map(type => type.name); const injectParameters: InjectParam[] = Reflect.getOwnMetadata(injectMetadataKey, target) || []; for (const param of injectParameters) { deps[param.index] = param.dependency; } const factory = (...args) => new target(...args); return [...deps, factory]; }
      
      





ここでは、 Reflect.getMetadata( "design:paramtypes"、target)を使用して、コンポーネントコンストラクターで受け入れられた引数のタイプに関する情報を引き出し(これを機能させるには、typeScriptトランスパイラーでオプションを有効にする必要があります-以下で詳しく説明します) type.nameからのIoCのファクトリ

次に、 injectParamateresについてもう少し説明します。 何らかのクラスインスタンスではなく、オブジェクトだけ、たとえばコンポーネントに渡されるアプリケーション構成やパラメーターを注入したい場合はどうでしょうか。 Angular2では、Injectデコレーターがこれに使用され、コンストラクターパラメーターに適用されます。

  constructor(@Inject("params") params, streetNameService: StreetNameService) { super(params, streetNameService); }
      
      





その実装は次のとおりです。

 interface InjectParam { index: number; dependency: string; } const injectMetadataKey = Symbol("inject"); export function Inject(token: string) { return (target: Object, propertyKey: string | symbol, parameterIndex: number) => { const existingInjectParameters: InjectParam[] = Reflect.getOwnMetadata(injectMetadataKey, target, propertyKey) || []; existingInjectParameters.push({ index: parameterIndex, dependency: token }); Reflect.defineMetadata(injectMetadataKey, existingInjectParameters, target, propertyKey); }; }
      
      





最後に、サービス登録デコレーター:

 export function Injectable() { return (target: { new (...args) }) => { if (!kontainer.isRegistered(target.name)) { const factory = getFactory(target); kontainer.registerFactory(target.name, factory); } }; } //  @Injectable() export class StreetNameService { constructor(config: AppConfig, @Inject("ApiClientFactory") apiClientFactory: ApiClientFactory) { this._apiClient = apiClientFactory(config.endpoints.streetName); } // ... }
      
      







すべてを作る方法は?



デコレータはまだ標準ではないため、 tsconfig.jsonファイルでデコレータを使用するには、 experimentalDecoratorsemitDecoratorMetadataを有効にする必要があります。

また、依存関係を登録するときにコンストラクター関数の名前に依存するため、UglifyJS設定でkeep_fnamesオプションを有効にすることが重要です。



ソースコードはこちらにあります



All Articles