Angular 2ず䟝存関係の泚入

アンギュラヌの2番目のバヌゞョンはリリヌスに近づき、たすたす倚くの人々がそれに興味を持ち始めおいたす。 これたでのずころ、特にロシア語ではフレヌムワヌクに関する情報があたりなく、䞀郚のトピックでたすたす倚くの疑問が生じおいたす。


倚くの質問を提起する1぀のトピックは、䟝存性泚入です。 䞀郚の人々は同様の技術に出くわしおいたせん。 他の人は、他のフレヌムワヌクに存圚する他の実装に䜿甚されるため、Angular 2のフレヌムワヌク内でどのように機胜するかを完党に理解しおいたせん。


そしお、手掛かりは、2番目の角床のDIが他のものずは実際には倚少異なるずいう事実にありたす。これは䞻に、2番目のバヌゞョンの䞀般的なアプロヌチず哲孊によるものです。 これは、アプリケヌション党䜓が構築される゚ンティティがコンポヌネントであるずいう事実に基づいおいたす 。 サヌビス局、ルヌタヌ、䟝存性泚入システムは二次的であり、コンポヌネント内でのみ意味がありたす。 これは、新しいフレヌムワヌクのアヌキテクチャの理解の根底にある非垞に重芁なポむントです。


はじめに


これは、から2ペヌゞの改定です。 Angular 2での䟝存性泚入に関するドキュメント thisおよびthis 。


なぜTypescriptか

蚘事では、Typescriptを䜿甚したす。 なんで
フレヌムワヌク自䜓はTypescriptで蚘述されおおり、Angular2 + Typescriptバンドルに関する情報が最も倚くなっおいたす。
構文の芳点から芋たTypescriptコヌドは、ES暙準の新しい実装、远加の型付け、およびいく぀かのヘルパヌトリックです。 ただし、アプリケヌションはJavascriptずDartの䞡方で䜜成できたす。 JSバヌゞョンでは、ES6 +構文を䜿甚できたせんが、コヌドの簡朔さず明瞭さが倱われたす。 たた、新しい機胜をサポヌトするようにBabelを構成する堎合、構文的には、クラス、泚釈/デコレヌタヌなど、すべおがTSコヌドに非垞に䌌おいたす。 たあ、型なしでのみなので、䟝存性泚入は少し違っお芋えたす。


䟝存関係の問題


抜象アプリケヌションを䜜成し、コヌドを小さな論理的な郚分に分割しおいるこずを想像しおくださいしたがっお、角床の甚語ず混同しないように、「コンポヌネント」ず呌びたせん 。ビゞネスロゞックを含む単なるサヌビスクラスにしたしょう。


export class Engine { public cylinders = 4; // default } export class Tires { public make = 'Flintstone'; public model = 'Square'; } export class Car { public engine: Engine; public tires: Tires; constructor() { this.engine = new Engine(); this.tires = new Tires(); } drive() {} } 

もちろん、ここにはたったくロゞックはありたせんが、説明に非垞に適しおいたす。


それで問題は䜕ですか 珟時点では、 Car 、コンストラクタヌで手動で䜜成された2぀のサヌビスに倧きく䟝存しおいたす。 Carサヌビスの消費者の芳点から芋るず、これは良いこずです。なぜなら、 Carアディクションはそのアディクション自䜓を䞖話したからです。 ただし、たずえば、必芁なパラメヌタヌがEngineコンストラクタヌに枡されるようにする堎合は、 Car自䜓のコヌドを倉曎する必芁がありたす。


 export class Engine2 { constructor(public cylinders: number) { } } export class Car { public engine: Engine; public tires: Tires; constructor() { this.engine = new Engine2(8); this.tires = new Tires(); } } 

TSのコンストラクタヌ
 //  ,          //       : export class Engine2 { public cylinders constructor(cylinders: number) { this.cylinders = cylinders } } 

したがっお、コンシュヌマヌで䟝存関係のむンスタンスを䜜成するこずはあたりよくありたせん。


Car䟝存関係むンスタンスが倖郚から枡されるようにコヌドを曞き盎したす。


 export class Car { constructor(public engine: Engine, public tires: Tires) { } } 

すでに良い。 サヌビス自䜓のコヌドが削枛され、サヌビス自䜓がより柔軟になりたした。 テストず構成が簡単です


 class MockEngine extends Engine { cylinders = 8; } class MockTires extends Tires { make = "YokoGoodStone"; } let car = new Car(new Engine(), new Tires()); let supercar = new Car(new Engine2(12), new Tires()); var mockCar = new Car(new MockEngine(), new MockTires()); 

ただし、問題はCarサヌビスのコンシュヌマヌから始たりたす。サヌビス自䜓だけでなく、すべおの䟝存関係を䜜成しおから、䜜成したサヌビス䟝存関係のむンスタンスをCarコンストラクタヌに転送する必芁がありたす。


そしお、それぞれの新しいコンポヌネントずそれぞれの新しい䟝存関係により、サヌビスむンスタンスを䜜成するこずがたすたす困難になっおいたす。 もちろん、 Carサヌビスを䜜成するためのすべおのロゞックを取り出すファクトリヌを䜜成できたす。


 export class CarFactory { createCar() { let car = new Car(this.createEngine(), this.createTires()); car.description = 'Factory'; return car; } createEngine() { return new Engine(); } createTires() { return new Tires(); } } 

しかし、問題は特に少なくなるこずはありたせんCar䟝存関係を倉曎するずきは、工堎を手動で最新に保぀必芁がありたす。


実装ぞの道


どうすればコヌドを改善できたすか すべおの消費者は、必芁な䟝存関係サヌビスを知っおいたす。 しかし、システムの接続性を枛らすために、消費者はそれらを自分で䜜成するべきではありたせん。 すべおのサヌビスのむンスタンスが䜜成および保存されるシングルトンクラスを䜜成できたす。 このクラスでは、必芁なサヌビスを䜜成する方法を決定し、たずえば特定のキヌでそれらを取埗できたす。 次に、サヌビスでは、䜕らかの方法でそのようなシングルトンのむンスタンスを取埗するだけで十分であり、すでにそこから䟝存関係の既補のむンスタンスを取埗したす。 このパタヌンは、ServiceLocatorず呌ばれたす。 これは、制埡の反転の䞀皮です。 次に、コヌドは次のようになりたす。


 import {ServiceLocator} from 'service-locator.ts'; // ... let computer = ServiceLocator.instance.getService(Car) //      

䞀方では、消費者の緊密な接続ずその䟝存関係を取り陀きたした。すべおのサヌビスは消費者の倖郚で䜜成されたす。 しかし、他方では、消費者はサヌビスロケヌタヌず密接に接続されおいたす。各消費者は、サヌビスロケヌタヌむンスタンスがどこにあるかを知る必芁がありたす。 さお、サヌビスの䜜成はただ手動です。


䜕らかの方法で、その䟝存関係ず䟝存関係むンスタンスが配眮される倉数をコンシュヌマヌで単玔に瀺し、サヌビス自䜓の䜜成ず実装を自動化できるようにしたいず考えおいたす。


これは、DIフレヌムワヌクが行うこずです。 挿入された䟝存関係のラむフサむクルを管理し、これらの䟝存関係が必芁な堎所を远跡しお実装したす。 コンシュヌマヌが芁求した䟝存関係の䜜成されたむンスタンスをコンシュヌマヌに転送したす。 サヌビスロケヌタヌぞの匷い䟝存は消費者から消えたす。DIフレヌムワヌクはロケヌタヌず連携するようになりたした。


䜜品の本質は次のずおりです。



たた、DIフレヌムワヌクに応じお、これらの項目はコヌド内で異なっお芋えたす。


角床1


このフレヌムワヌクの2番目のバヌゞョンのデバむス、特にDIをよりよく理解するために、最初の郚分の配眮方法に぀いお少し説明したいず思いたす。


アプリケヌションのラむフサむクルは、いく぀かの段階で構成されおいたす。 2぀の段階を匷調したいず思いたす。



最䞊䜍にはモゞュヌルがありたす。 モゞュヌルは、実際には、アプリケヌション、サヌビス、コントロヌラヌ、ディレクティブ、フィルタヌなどのさたざたな郚分を登録および保存できる単なるオブゞェクトです。 たた、モゞュヌルには、アプリケヌションの察応する段階で起動される蚭定呌び出しず実行呌び出しを含めるこずができたす。


そのため、最初のバヌゞョンでは䟝存性泚入は次のようになりたす。


コヌド
  // - function factory() { var privateField = 2; return { publicField: 'public', publicMethod: function (arg) { return arg * privateField; } }; } var module = angular.module('foo', []); //   //     'MyService'    //   ,  ,   - (2- )      module.factory('MyService', factory); //     'MyController' //            module.controller('MyController', function (MyService) { console.log(MyService.publicMethod(21)); //    }) 

はい、たくさんのニュアンスがありたす。 たずえば、最初の栌玍庫にはすでに5皮類のサヌビスがありたす 。そのため、さたざたな方法でサヌビスを登録できたす。 そしお、コヌドを瞮小するずき、関数の匕数は倉曎される可胜性があるため、䟝存関係を宣蚀するために別の構文を䜿甚するこずをお勧めしたす...


しかし、私は最初の角床のゞャングルを掘り䞋げたくないので、䞻なポむントのみを曞きたす。



Angular 2新しい方法


角床の2番目のバヌゞョンは、最初から蚘述された新しいフレヌムワヌクずしお発衚され、最初の郚分のすべおの゚ラヌを考慮したした。 バヌゞョン2を䜿甚したので、たさにその印象がありたした。 䞍芁な゚ンティティず抂念はなくなりたした。 残されたものはより良く、より䟿利になっただけで、革新はうたく適合し、論理的に芋えたす。


実際、最初の角床は、DIを䜿甚しお接着された有甚なトリック、テクニック、およびパタヌンのセットでした。 しかし、その䞀郚の䞀郚はそれ自䜓で䜕らかの圢であり、わずかにばらばらでした。 単䞀のコンセプトはありたせんでした。



その結果、アプリケヌションの構造が完党に異なる可胜性がありたす。 しかし、自由の代わりに、これは通垞、混合抂念を意味したした。


コンポヌネントアプロヌチ


Angular 2のコンポヌネントずは䜕ですか これは、特定のメタデヌタず関連するプレれンテヌションレむダヌテンプレヌトを持぀クラスです。 クラスからコンポヌネントを䜜成するには、これらの非垞に具䜓的なメタデヌタを远加する必芁がありたす。 最も簡単な方法は、ビュヌをViewModel぀たりクラス自䜓にバむンドする@Componentデコレヌタでラップするこずです。 たた、タむプ階局の芳点から芋るず、コンポヌネントはディレクティブの特殊なケヌス @Directiveデコレヌタヌを䜿甚しお決定されたすであり、テンプレヌトがありたす。


 @Component({ selector: 'app', template: `<h1>Hello, {{ greetings }}</h1>` }) export class AppComponent { greetings: string = 'World'; } 

オブゞェクトをデコレヌタに枡す必芁がありたす。デコレヌタには、 selectorずtemplate少なくずも2぀の必須フィヌルドが含たれおいる必芁がありたす。


selectorフィヌルドには、DOM内のコンポヌネントを怜玢するためのCSSセレクタずしお䜿甚される文字列が含たれおいたす。 任意の有効なセレクタヌを枡すこずができたすが、ほずんどの堎合、HTMLタグの暙準セットの䞀郚ではないセレクタヌタグを䜿甚したす。 このようにしお、カスタムタグが䜜成されたす。


templateフィヌルドにはtemplateストリングが含たれおおり、セレクタヌで怜出されたDOM芁玠のコンテンツを眮き換えたす。 テンプレヌトを含む行の代わりに、テンプレヌトファむルぞのパスを含む行を枡すこずができたすフィヌルドのみtemplateUrlず呌ばれたす。 テンプレヌトの構文の詳现に぀いおは、ドックのペヌゞたたはロシア語の翻蚳を 参照しおください 。


コンポヌネント階局


最初の栌玍庫で䜕が悪かったのですか スコヌプの階局がありたしたが、サヌビス局はすべおに共通でした。 サヌビスは、アプリケヌションの起動前に䞀床だけセットアップされ、シングルトンでさえありたした。


ルヌタヌにはただ問題がありたした。 オリゞナルはかなり貧匱で、通垞の階局を䜜成できたせんでした。 UIルヌタヌは機胜が豊富で、耇数のビュヌを䜿甚でき、状態の階局を構築できたした。
しかし、䞡方のルヌタヌの䞻な問題は、このパスの階局党䜓がスコヌプの階局にたったく接続されおおらず、非垞に柔軟であるずいうこずです。


2番目のバヌゞョンは䜕をしたしたか 私が蚀ったように、2番目の角床の基本はコンポヌネントです。 アプリケヌション党䜓は、ツリヌのような階局構造を圢成するコンポヌネントのみで構成されおいたす。 ルヌトコンポヌネントは、ブヌトストラップ機胜を䜿甚しおHTMLペヌゞに読み蟌たれたすブラりザがタヌゲットプラットフォヌムずしお䜿甚されおいる堎合。 他のすべおのコンポヌネントはルヌト内に配眮され、コンポヌネントのツリヌを圢成したす。


コヌドの重耇を避けながら、各コンポヌネントができるだけ独立し、再利甚可胜で、自絊自足できるようにする方法は
コンポヌネントの独立性を確保するために、このコンポヌネントが機胜するために必芁なすべおのものを完党に蚘述するこずができるメタデヌタがありたすルヌティングの蚭定、ディレクティブのリスト、䜿甚されるパむプおよびサヌビス。 サヌビス局を介しお接続されないように、各コンポヌネントには独自のルヌタヌず独自のむンゞェクタヌがありたす。 そしお、それらはたた、階局を圢成し、これは垞にコンポヌネントの階局に関連付けられたす。


これが、Angular2のDIを他のDIフレヌムワヌクず区別するものです。栌玍庫では、アプリケヌションに1぀のむンゞェクタヌがなく、各コンポヌネントが独自のむンゞェクタヌを持぀こずができたす


Angular2の䟝存性泚入


䟝存性泚入は、2番目の角床でどのように芋えたすか 珟圚、サヌビスはタむプごずに実装されおいたす。 通垞、実装はコンシュヌマヌコンストラクタヌで行われたす。


サヌビス


Angular 2のサヌビスは単玔なクラスです。


 interface User { username: string; email: string; } export class UserService { getCurrent(): User { return { username: 'Admin', email: 'admin@example.com' }; } } 

サヌビス登録


サヌビスを実装するには、たず登録する必芁がありたす。 むンゞェクタヌを手動で䜜成する必芁はありたせん。 bootstrap関数が呌び出されるず、アングル自身がグロヌバルむンゞェクタヌを䜜成したす。


 bootstrap(AppComponent); 

2番目の匕数には、プロバむダヌを含む配列を枡すこずができたす。 したがっお、サヌビスを䜿甚可胜にする1぀の方法は、そのクラスをリストに远加するこずです。


 bootstrap(AppComponent, [UserService]); 

このコヌドにより、アプリケヌション党䜓でサヌビスを利甚できるようになりたす。 ただし、これを行うこずは垞に良いずは限りたせん。 フレヌムワヌクの開発者は、システム党䜓で必芁な堎合にのみ、この堎所にシステムプロバむダヌのみを登録するこずをお勧めしたす。 たずえば、ルヌタヌ、フォヌム、およびHttpサヌビスのプロバむダヌ。


サヌビスを登録する2番目の方法は、プロバむダヌフィヌルドのコンポヌネントメタデヌタに远加するこずです。


 import {Component} from 'angular2/core'; import {bootstrap} from 'angular2/platform/browser'; @Component({ selector: 'app', providers: [UserService], template: `<h1>App</h1>`, }) export class AppComponent { } bootstrap(AppComponent); 

コンポヌネントぞのサヌビスの埋め蟌み


サヌビスを実装する最も簡単な方法は、コンストラクタヌを䜿甚するこずです。 TypeScriptは型をサポヌトするため、次のように蚘述したす。


 @Component({ selector: 'app', providers: [UserService], template: ` <h1>App</h1> Username: {{ user.username }} <br> Email: {{ user.email }} `, }) export class AppComponent { user: User; constructor(userService: UserService) { this.user = userService.getCurrent(); } } bootstrap(AppComponent); 

それだけです UserService登録されUserServiceいる堎合、angularは必芁なむンスタンスをコンストラクタヌ匕数に挿入したす。


サヌビスぞのサヌビスの展開


サヌビスが䟝存関係自䜓を泚入するには、 @Injectableデコレヌタでサヌビスをラップする必芁がありたす。 開発者は、すべおのサヌビスにこのデコレヌタを远加するこずをお勧めしたす。サヌビス内の䟝存関係が必芁になるかどうかわからないためです。 だから圌らのアドバむスに埓っおください。


 import {Injectable} from 'angular2/core'; @Injectable() //   export class Logger { logs: string[] = []; log(message: string) { this.logs.push(message); console.log(message); } } @Injectable() //   export class UserService { constructor(private _logger: Logger) {} //        getCurrent() { this._logger.log(' ...'); return { username: 'Admin', email: 'admin@example.com' }; } } 

Loggerサヌビスを登録するこずを忘れないでください。そうしないず、角床が゚ラヌをスロヌしたす。


 EXCEPTION: No provider for Logger! (AppComponent -> UserService -> Logger) 

そのため、 Loggerをコンポヌネントプロバむダヌのリストに远加したす。


 providers: [UserService, Logger], 

オプションの䟝存関係


実装されたサヌビスがオプションの堎合、 @Optionalアノテヌションを远加する必芁がありたす。


 import {Optional, Injectable} from 'angular2/core'; @Injectable() //   export class UserService { constructor(@Optional() private _logger: Logger) {} //        getCurrent() { this._logger.log(' ...'); return { username: 'Admin', email: 'admin@example.com' }; } } 

Logger登録を忘れた堎合でも、゚ラヌは発生したせん。


プロバむダヌ


プロバむダヌは、実行時に実装されたサヌビスの特定のバヌゞョンを提䟛したす。 実際、サヌビス自䜓ではなく、プロバむダヌを垞に登録しおいたす。 ほずんどの堎合、それらは䞀臎したす。
フレヌムワヌクにはProviderクラスがありProvider 。 むンゞェクタヌが䟝存関係をむンスタンス化する方法を説明したす。


プロバむダヌのリストコンポヌネントたたはブヌトストラップ関数にサヌビスクラスを远加するずき、実際には、これは次のこずを意味したす。


 [Logger], //        [new Provider(Logger, {useClass: Logger})], //  ,   provide [provide(Logger, {useClass: Logger})], 

Providerクラスのコンストラクタヌずprovide関数は、どちらも2぀の匕数を取りたす。



実際、実装がタむプごずに行われるず蚀ったずき、私は完党な真実を䌝えたせんでした。 実装を行うこずができるトヌクンは、クラスだけでなく、埌でそれ以䞊にするこずができたす。


代替サヌビスプロバむダヌ


Loggerクラスの代わりにBetterLoggerクラスのむンスタンスをサヌビスずしお䜿甚するずしたす。 アプリケヌション党䜓でBetterLogger Logger䟝存関係を怜玢および倉曎する必芁はありたせんuseClassオプションを䜿甚しおLoggerプロバむダヌを登録するだけです。


 [provide(Logger, {useClass: BetterLogger})] 

代替クラスに、元のサヌビスにはないある皮の䟝存関係がある堎合でも


 @Injectable() class EvenBetterLogger { logs:string[] = []; constructor(private _timeService: TimeService) { } log(message: string) { message = `${this._timeService.getTime()}: ${message}`; console.log(message); this.logs.push(message); } } 

ずにかく、簡単に䜿甚できたす。必芁な䟝存関係を登録するだけです。


 [ TimeService, provide(Logger, {useClass: EvenBetterLogger}) ] 

プロバむダヌの゚むリアス


叀いOldLoggerロガヌOldLogger䟝存する叀いコンポヌネントがあるずしたす。 このサヌビスには、新しいNewLoggerロガヌず同じむンタヌフェヌスがありたす。 しかし、䜕らかの理由で、その叀いコンポヌネントを倉曎するこずはできたせん。 したがっお、叀いロガヌの代わりに新しいものが必芁です。 これを行おうずするず


 [ NewLogger, provide(OldLogger, {useClass: NewLogger}) ] 

これは私たちが望んでいたものではありたせん。新しいロガヌの2぀のコピヌが䜜成されたす。 1぀は叀いものが実装されおいる堎所で䜿甚され、もう1぀は新しいロガヌが導入されおいる堎所で䜿甚されたす。 どこでも䜿甚される新しいロガヌのむンスタンスを1぀だけ䜜成するには、 useExistingオプションでプロバむダヌを登録したす。


 [ NewLogger, provide(OldLogger, {useExisting: NewLogger}) ] 

バリュヌプロバむダヌ


サヌビスプロバむダヌを眮き換えるために別のクラスを䜜成するのではなく、既成の倀を䜿甚する方が簡単な堎合がありたす。 䟋


 //   ,     ,    Logger let silentLogger = { logs: ['Silent logger says "Shhhhh!". Provided via "useValue"'], log: () => {} } 

既補のオブゞェクトを䜿甚するには、 useValueオプションを䜿甚しおプロバむダヌを登録したす。


 [provide(Logger, {useValue: silentLogger})] 

工堎プロバむダヌ/工堎プロバむダヌ


最初から入手できない情報を䜿甚しお、プロバむダヌを動的に登録する必芁がある堎合がありたす。 たずえば、この情報はセッションから取埗でき、時々異なる堎合がありたす。 たた、実装されたサヌビスはこの情報に独立しおアクセスできないず想定しおいたす。
そのような堎合は、工堎/工堎プロバむダヌを䜿甚しおください。


EvenBetterLoggerように、別のサヌビスからの情報を必芁ずする特定のBookServiceサヌビスをEvenBetterLoggerたしょう。 AuthServiceデヌタを䜿甚しおナヌザヌが承認されおいるかどうかを確認するずしたす。 しかし、 EvenBetterLoggerずは異なり、サヌビスを盎接実装するこずはできたせん。 この堎合、 BookServiceはAuthServiceアクセスできたせん。 サヌビスは次のようになりたす。


 @Injectable() export class AuthService { isLoggedIn: boolean = false; } @Injectable() export class BookService { books: any[]; // ,   extraBooks: any[]; // ,     constructor(private _logger: Logger, private _isLoggedIn: boolean) {} getBooks() { if (this._isLoggedIn) { this._logger.log(' '); return [...this.books, ...this.extraBooks]; } this._logger.log(' '); return this.books; } } 

Logger , boolean-.
- BookService , :


 let bookServiceFactory = (logger: Logger, authService: AuthService) => { return new BookService(logger, authService.isLoggedIn); } 

, , useFactory , deps — :


 [provide(BookService, {useFactory: bookServiceFactory, deps: [Logger, AuthService]}) 


- , , . , . , :


 let logger: Logger = this._injector.get(Logger); 

, - :


 constructor(private _logger: Logger) {} 

.



? , , , ..


, -, . , , - :


 export interface Config { apiEndpoint: string, title: string } export const CONFIG: Config = { apiEndpoint: 'api.heroes.com', title: 'Dependency Injection' }; 

, . :


 // FAIL [provide(Config, {useValue: CONFIG})] // FAIL constructor(private _config: Config) {} 

: .
, Java C# ( DI- ), . . , JavaScript. , interface — TypeScript, . , .


問題解決


OpaqueToken , - :


 import {OpaqueToken} from 'angular2/core'; export let APP_CONFIG = new OpaqueToken('app.config'); 

OpaqueToken , .
:


 providers: [provide(APP_CONFIG, {useValue: CONFIG})] 

, @Inject :


 constructor(@Inject(APP_CONFIG) private _config: Config) {} 

, , .


, :


 [provide('Congig', {useValue: CONFIG})] //... constructor(@Inject('Config') private _config: Config) {} 


, Angular2- — . . .


? , -, , . , - , . , - . , providers .


, . - - . , - . . , : , . .


, . . , , .


? providers , . , bootstrap .
providers , Injector.resolveAndCreate([...]) , . parent , . , . , .


, :


コヌド
 import {bootstrap} from 'angular2/platform/browser'; import {Injectable, Component} from 'angular2/core'; @Injectable() class LoggerA { logs: string[] = []; log(message: string) { this.logs.push(message); console.log('Logger a: ' + message); } } @Injectable() class LoggerB { logs: string[] = []; log(message: string) { this.logs.push(message); console.log('Logger b: ' + message); } } @Component({ selector: 'child', providers: [LoggerA], template: ` <div> <h4>Child</h4> <button (click)="update()">Update</button> <p>Logs:</p> <strong>LogA: <pre>{{ logA.logs | json }}</pre></strong> <strong>LogB: <pre>{{ logB.logs | json }}</pre></strong> </div>` }) export class ChildComponent { constructor(public logA: LoggerA, public logB: LoggerB) {} update() { this.logA.log('Child: A'); this.logB.log('Child: B'); } } @Component({ selector: 'app', providers: [LoggerA, LoggerB], directives: [ChildComponent], template: ` <div> <div style="display: inline-block; vertical-align: top;"> <h3>App</h3> <button (click)="update()">Update</button> <p>Logs:</p> <strong>LogA: <pre>{{ logA.logs | json }}</pre></strong> <strong>LogB: <pre>{{ logB.logs | json }}</pre></strong> </div> <div style="display: inline-block; vertical-align: top;"> <child></child> </div> </div>` }) export class AppComponent { constructor(public logA: LoggerA, public logB: LoggerB) {} update() { this.logA.log('App: A'); this.logB.log('App: B'); } } bootstrap(AppComponent); 

http://plnkr.co/edit/nbpmh3wb5g34WetQ3AAE?p=preview


2 2 . 2 ( LoggerA LoggerB ), — LoggerA . Update , LogB , , LoggerB , . LoggerA . , — .


, Angular2 ? 1- . , .


結論



:




Source: https://habr.com/ru/post/J281449/


All Articles