Spasは、サーバー側のレンダリングされたWebサイトに対するアンチテーゼとしてトレンディになりました。いつものように長所と短所がありますが、スパはそのようなアプローチなしで複製するのが難しいスムーズなユーザーエクスペリエンスを作成できます。これは、デスクトップアプリケーションで伝統的に占有されていたWebアプリケーションの領域へのベンチャーの一部です。 Webアプリケーションは一般に、デスクトップアプリと比較して鈍化していると批判されていますが、Webテクノロジー(特にNodejsとGoogleのV8エンジン、AngularやReactjsなどのフレームワーク)の大きな進歩と、前例のないコンピューティングパワーへの主流のアクセスは、これが非常に重要であることを意味します。以前よりも問題が少ない。
クライアントに単一のHTMLページをロードして、ユーザーがクリックするたびにサーバーから更新されたHTMLページを取得するのではなく、コンテンツ(およびDOM)を動的に更新することにより、スパが機能します。
NodeJS
とnpm
インストールします。TypeScript
をインストールします。npm install -g @angular/cli
ng new my-project
cd my-project && ng serve
使用してローカルにアプリを提供するlocalhost:4200
in you Webブラウザにアクセスできます。my-project/src/app/app.component.ts
を開いている場合は、次のようなもので迎えられる必要があります。
/** app.component.ts */
import { Component } from '@angular/core' ; // #1
@ Component ( { // #2
selector : 'app-root' , // #3
templateUrl : './app.component.html' , // #4
styleUrls : [ './app.component.css' ] // #5
} )
export class AppComponent {
title = 'app' ; // #6
}
これを少し分解しましょう:
node_modules
フォルダーにある@angular
モジュールからComponent
インターフェイスをインポートします。@Component
デコレーターは、クラスを角度成分としてマークします。デコレーターは、オプションオブジェクトでパラメーター化されており、そのパラメーターはいくつかのパラメーターのみを使用します。selector
パラメーターは、そのコンポーネントのHTMLタグが何であるかを定義します - たとえば、これは<app-root> … </app-root>
を使用してHTMLに挿入されます。templateUrl
パラメーターは、コンポーネントのビュー部分として機能するHTMLファイルを指す文字列引数を取ります。ファイルを指すのではなく、 template
パラメーターを使用してHTMLを直接書き込むこともできます。ビュー部分が単に1行か2行でない限り、これは一般的に推奨されません。styleUrls
パラメーターには、各文字列がCSSファイルへのパスである文字列のリストを採用します。title
app.component.html
から参照できます。app.component.html
に移動します。これがアプリケーションのエントリポイントであり、編集する必要がある最高レベルのHTMLです。このコンテキスト内ですべてがレンダリングされます。
次に、このファイルの内容を削除し、次のファイルに置き換えます。
< div class =" container " >
< h1 > Live football scores </ h1 >
< div class =" score-card-list " >
< div class =" score-card " >
< span > Team 1 </ span >
< span > 0 : 0 </ span >
< span > Team 2 </ span >
</ div >
</ div >
</ div >
Webブラウザに戻ると、ページが自動的に更新されたことがわかります。素晴らしい。
視覚的には、この段階ではおそらく失望するでしょうが、進行中の作業です。
Angular Materialは、Angularアプリケーション向けに使いやすい材料設計コンポーネントを提供するために、Googleのチームによって維持されているライブラリです。これらのコンポーネントのスタイリングは、心のコンテンツに微調整できますが、プロトタイプアプリの外観と感触を最小限の労力で簡単にアップグレードする簡単な方法も提供します。
npm install --save @angular/material @angular/cdk @angular/animations
。これにより、必要なファイルをnode_modules
フォルダーにダウンロードし、 packages.json
ファイルを適切に更新します。
また、関連するコンポーネントをロードするようにAngularに指示する必要があります。これがどのように機能するか(これは101ダクトテープの種類のチュートリアルです)についてグーグルで行う必要がありますが、基本的にはapp.modules.ts
ファイルを変更して、必要なモジュールを含める必要があります。
まず、次のようなアニメーションを追加します。
import { BrowserAnimationsModule } from '@angular/platform-browser/animations' ;
@ NgModule ( {
...
imports : [ BrowserAnimationsModule ] ,
...
} )
次に、必要なモジュールをインポートします。
import { MatButtonModule , MatCardModule } from '@angular/material' ;
@ NgModule ( {
...
imports : [ MatButtonModule , MatCardModule ] ,
...
} )
アプリで使用するコンポーネントをインポートする必要があることに注意してください。ライブラリ全体を含めない理由は、 webpack
ツリーシェーキングを実行できるようにするためです。これは、基本的に、すべてのコードをいくつかの.js
ミニフォームファイルにバンドするときに未使用のコードを残すことを伴います。
これが完了すると、事前定義された多数のコンポーネントにアクセスでき、必要に応じて単純にインポートできます。また、事前に構築されたテーマが使いやすいことも注目に値します。
Angularでは、コンポーネントはモジュラーです。各コンポーネントには独自のCSSファイルがあり、その特定のビューにのみ適用されます。 1つの例外は、Globalで適用されるsrc
ディレクトリにあるstyles.css
ファイルです。
スタイリングはこのチュートリアルの焦点ではないため、今後可能な限り回避しようとします。私たちが作っているものについて少し良く感じるために、ここにstyles.css
ファイルにコピーして貼り付ける簡単なCSSがあります(適切なコンポーネントのCSSファイルに関連するビットを追加することで、今後の参照のために):
// Outer wrapper div
. container {
text-align : center;
}
// Wrapper for the score cards
. score-card-list {
display : flex;
flex-direction : column;
align-items : center;
}
// Each score card
. score-card {
width : 550 px ;
display : flex !important ;
}
// Home team score
. home {
flex : 1 ;
text-align : right;
}
// Away team score
. away {
flex : 1 ;
text-align : left;
}
// Score section of score card
. score {
width : 100 px ;
}
また、HTMLを更新してクラスを含めます。
...
< mat-card class =" score-card " >
< span class =" home " > Team 1 </ span >
< span class =" score " > 0 : 0 </ span >
< span class =" away " > Team 2 </ span >
</ mat-card >
...
flex
について混乱している場合は、FlexBox Froggyをチェックすることを強くお勧めします。
Angularのようなフレームワークを使用することの利点の1つは、HTMLにいくつかのプログラミングロジックを直接実装できることです。私たちの場合、For-Loopsは生活をずっと楽にします。
以下を試してみてください。
...
< mat-card class =" score-card " *ngFor =" let team of ['Arsenal', 'Liverpool', 'Tottenham']; let i=index " >
< span class =" home " > {{ team }} </ span >
< span class =" score " > 0 : 0 </ span >
< span class =" away " > Team {{ i+1 }} </ span >
</ mat-card >
...
これにより、さまざまな新しい概念が導入され、次のようなものを作成する必要があります。
注意すべき概念:
{{ 'Some text = ' + a_number }}
サービスは、コンポーネントから離れてデータアクセスを抽象化するために使用されます。これにより、コンポーネントは無駄のないままで、ビューのサポートに集中できます。この責任の分離を維持することにより、単体テストとコードメンテナンスが簡素化されます。
CLIを使用してサービスを生成するにはng gs football-data
ng generate service football-data
使用することができます。これにより、 project-dir/src/app
ディレクトリに2つの新しいファイルが作成されます: football-data.service.ts
およびfootball-data.service.spec.ts
。これは、テストファイルが.spec.ts
拡張子を取得する角度条約に続きます。テストは有用で重要ですが、このチュートリアルの即時の範囲の外側にあるので、今のところ無視することができます(ごめんなさい、@cornel)。
football-data.service.ts
に次の行を追加します。
import { Injectable } from '@angular/core' ;
import { HttpHeaders } from '@angular/common/http' ;
@ Injectable ( ) // Designate this class as an injectable service
export class FootballDataService {
// Token for accessing data on football-data.org (see: https://www.football-data.org/client/register)
HEADERS = new HttpHeaders ( { 'X-Auth-Token' : 'dc25ff8a05123411sadgvde5bb16lklnmc7' } ) ;
// Convenience constant
COMPETITION_URL = 'http://api.football-data.org/v1/competitions/' ;
// ID for the Premier League
PL_ID = 445 ;
constructor ( ) { }
}
サービスには@Injectable
が注釈が付けられており、サービスのインスタンスをコンポーネントに注入できることをコンパイラに伝えます。また、各サービスのプロバイダーを指定する必要があります。プロバイダーは、指定されたレベルでシングルトンを効果的に提供します。
たとえば、 app.module.ts
のプロバイダー配列にサービスを追加できます。その結果、アプリ全体でサービスが公開されているシングルトンインスタンスが発生します。それ以外の場合は、コンポーネントのプロバイダーリストに追加できます。その結果、シングルトンインスタンスがそのコンポーネントとそのコンテキスト内のコンポーネントが利用可能になります。
警告:アプリが大きくなると、サービス間で循環依存関係を簡単に作成できます。これに注意してください。 Cannot resolve all parameters for MyDataService(?)
ようなエラーメッセージを取得した場合、それはおそらく循環依存関係の問題に関連しています。
Football-data.orgで自由に利用できる素晴らしいAPIを利用します。独自のAPIキーを取得する必要がありますが、簡単に実行できます。サイトの指示に従ってください。さらに詳細なドキュメントがありますが、必要なものの99%は、ここにリストされている小さな例で見ることができます。
この例では、私たちが本当にやりたいことは、現在のラウンド(別名「ゲームウィーク」または「マッチデイ」)のすべてのゲームのゲーム情報を取得することだけです。 /v1/competitions/{id}/fixtures
エンドポイントはこの情報を返しますが、現在のシーズンのすべての過去のラウンドについて。 1回のラウンドの情報を取得するには、 matchday
パラメーターを設定する必要があります。たとえば/v1/competitions/{id}/fixtures?matchday=14
。
現在のマッチデーを獲得するために、リーグテーブルはデフォルトで現在のマッチデイに戻るため、リーグテーブルを尋ねることができます。
まず、AngularのHTTP関数を使用するために、 HttpClient
サービスをFootballDataService
に注入する必要があります。
import { HttpClient , HttpHeaders } from '@angular/common/http' ;
...
constructor ( private http : HttpClient ) { }
. . .
重要:Angularサービスまたはコンポーネントのコンストラクターにプライベート変数を追加することと、特定のタイプスクリプトタイプの宣言とともに、Angularのブラックマジックが機能するのに十分な情報です。コンパイラがこのサービスに適切なインスタンスを挿入するようになるため、アクセスできます。
サーバーからリーグテーブル(および現在の試合日)を取得する関数を追加しましょう。
...
getTable ( ) {
return this . http . get ( this . COMPETITION_URL + this . PL_ID + '/leagueTable' ,
{ headers : this . HEADERS } ) ;
}
...
タイプスクリプトはこれを使用しますが、Angularのhttp.get
メソッドのメソッド署名は次のように見えます。
/**
* Construct a GET request which interprets the body as JSON and returns it.
*
* @return an `Observable` of the body as an `Object`.
*/
get ( url : string , options ?: {
headers ?: HttpHeaders | {
[ header : string ] : string | string [ ] ;
} ;
observe?: 'body' ;
params?: HttpParams | {
[ param : string ] : string | string [ ] ;
} ;
reportProgress?: boolean ;
responseType?: 'json' ;
withCredentials?: boolean ;
} ) : Observable < Object > ;
疑問符は、パラメーターheaders
、 observe
、 params
、 reportProgress
、 responseType
、およびwithCredentials
options
のオプションオブジェクトの一部としてすべてオプションであることを示しています。 url
とoptions.headers
の値を渡すだけです。
returnを作成したばかりのgetTable()
関数が何であるか疑問に思うかもしれません。まあ、それはObservable
ストリームを返します。 Luuk Gruijsが言ったように、 Observable
Sは本質的に「時間の経過とともに複数の値の怠zyなコレクション」です。クレイジーに聞こえますが、実際にはかなり簡単です。
要するに、 Observable
ストリームは、サブスクライブになるまで「コールド」と見なされます。つまり、変数は出力が使用されると怠lazにのみロードされます。ストリームが「ホット」になると、値が変更されるたびにすべてのサブスクライバーが更新されます。これは、JavaScriptの非同期状況を処理するためにPromise
を使用する代わりになります。
この場合、RESTインターフェイスは通話ごとに単一の値を返すだけなので、 Observable
変数がサブスクライブされると、 GET
リクエストが起動されます。
タイプスクリプトの静的タイピングの力を使用して、これをより明確にすることができます。
...
import { Observable } from 'rxjs/Observable' ;
...
getTable ( ) : Observable < any > {
return this . http . get ( this . COMPETITION_URL + this . PL_ID + '/leagueTable' ,
{ headers : this . HEADERS } ) ;
}
...
実際、Football-Data.orgのドキュメントは、残りの呼び出しから何を期待するかを正確に示しているため、 src/app/models/leagueTable.ts
で、さらに一歩進んでオブジェクトをモデル化することができます。
import { Team } from './team' ;
export class LeagueTable {
leagueCaption : string ;
matchday : number ;
standing : Team [ ] ;
}
およびsrc/app/models/team.ts
:
export class Team {
teamName : string ;
crestURI : string ;
position : number ;
points : number ;
playedGames : number ;
home : {
goals : number ;
goalsAgainst : number ;
wins : number ;
draws : number ;
losses : number ;
} ;
away : {
goals : number ;
goalsAgainst : number ;
wins : number ;
draws : number ;
losses : number ;
} ;
draws : number ;
goalDifference : number ;
goals : number ;
goalsAgainst : number ;
losses : number ;
wins : number ;
}
これにより、 football-data.service.ts
以下に更新できます。
import 'rxjs/add/operator/map' ;
import { LeagueTable } from './models/leagueTable' ;
...
getTable ( ) : Observable < LeagueTable > {
return this . http . get ( this . COMPETITION_URL + this . PL_ID + '/leagueTable' ,
{ headers : this . HEADERS } )
. map ( res => res as LeagueTable ) ;
}
...
これは、IDEが私たちを導くことができるので、複雑なオブジェクトを操作しながら最新の状態を維持するために必要なメンタルモデルを最小限に抑えることにより、私たちの正気を維持するのに役立ちます。
サイドノート: as
キーワードは、ある種の検査でそれを理解しようとするのではなく、オブジェクトのタイプについて私たちに信頼するようにTypeScriptに単純に指示します。最も興味深いもののように、危険ですが便利です。
さて、 src/app/app.component.ts
に戻り、 FootballDataService
コンポーネントに注入するために次の行を追加します。
import { FootballDataService } from './football-data.service' ;
...
export class AppComponent {
title = 'app' ;
constructor ( private footballData : FootballDataService ) { }
}
また、コンポーネントにngOnInit
メソッドを追加します。これは、Angularが提供する標準のライフサイクルフックであり、コンポーネントのすべてのデータ結合プロパティが初期化された後に発火します。基本的にはオブジェクトの初期化に火をつけますが、コンポーネントへのすべての入力と出力が登録される前に発火するconstructor
メソッドよりもわずかに遅れます。
一般的な経験則は、コンストラクターではなく、この特別なngOnInit
メソッドの初期化で呼び出したいコードを常に配置することです。次のようなコンポーネントに追加します:
import { Component , OnInit } from '@angular/core' ;
...
export class AppComponent implements OnInit {
...
constructor ( private footballData : FootballDataService ) { }
ngOnInit ( ) {
// Code you want to invoke on initialisation goes here
}
...
私たちの場合、リーグのテーブルをロードしたいので、次のようなものを追加できます。
...
ngOnInit ( ) {
// Load league table from REST service
this . footballData . getTable ( )
. subscribe (
data => console . log ( data ) ,
error => console . log ( error )
) ;
}
...
Webブラウザでコンソールを開くと、次のようなものが表示されるはずです。
素晴らしい。今では、将来的に何かをすることができる多くのクールなデータがあります(特にクラブクレスト画像への直接リンク)がありますが、今のところは今のところ、現在の試合に本当に興味があります。
次に、データサービスに別の関数を追加して、現在の備品のラウンドの情報を取得しましょう。
...
import { GameWeek } from './models/gameWeek' ;
...
getFixtures ( matchDay : number ) : Observable < GameWeek > {
return this . http . get ( this . COMPETITION_URL + this . PL_ID + '/fixtures?matchday=' + matchDay , { headers : this . HEADERS } )
. map ( res => res as GameWeek ) ;
}
...
GameWeek
とFixture
次のように定義されています。
// src/app/models/gameWeek.ts
import { Fixture } from './fixture'
export class GameWeek {
count : number ;
fixtures : Fixture [ ] ;
}
// src/app/models/fixture.ts
export class Fixture {
awayTeamName : string ;
date : string ;
homeTeamName : string ;
matchday : number ;
result : {
goalsAwayTeam : number ;
goalsHomeTeam : number ;
halfTime : {
goalsAwayTeam : number ;
goalsHomeTeam : number ;
}
} ;
status : 'SCHEDULED' | 'TIMED' | 'POSTPONED' | 'IN_PLAY' | 'CANCELED' | 'FINISHED' ;
_links : {
awayTeam : { href : string ; } ;
competition : { href : string ; } ;
homeTeam : { href : string ; } ;
self : { href : string ; } ;
} ;
}
新しく獲得したマッチデイの知識により、RESTサーバーに現在の備品のラウンドに関する情報を尋ねることができます。ただし、最初の休憩コールが最初に完了するのを待つ必要があります。一部のリファクタリングとは、コールバックでかなり簡単にできることを意味します。
import { Component , OnInit } from '@angular/core' ;
import { FootballDataService } from './football-data.service' ;
import { LeagueTable } from './models/leagueTable' ;
@ Component ( {
selector : 'app-root' ,
templateUrl : './app.component.html' ,
styleUrls : [ './app.component.css' ] ,
providers : [ FootballDataService ]
} )
export class AppComponent implements OnInit {
title = 'app' ;
table : LeagueTable ;
gameweek : GameWeek ;
constructor ( private footballData : FootballDataService ) { }
ngOnInit ( ) {
this . getTable ( ) ;
}
getTable ( ) {
this . footballData . getTable ( )
. subscribe (
data => {
this . table = data ; // Note that we store the data locally
this . getFixtures ( data . matchday ) ; // Call this function only after receiving data from the server
} ,
error => console . log ( error )
) ;
}
getFixtures ( matchDay : number ) {
this . footballData . getFixtures ( matchDay )
. subscribe (
data => this . gameweek = data , // Again, save locally
error => console . log ( error )
) ;
}
}
今、私たちはどこかになっています!
タイプスクリプトコンポーネントのメンバー変数としてデータを設定しているため、関連するHTMLから直接アクセスできます。実際、MicrosoftのオープンソースエディターであるVisual Studio Codeを使用している場合、Angular Language Serviceプラグインを追加してHTMLでJavaScriptコードの完了を取得できます。すばらしい。そして、それはAngularチームによって維持されます。
以前からダミーデータを置き換えましょう。
< div class =" container " >
< h1 > Live football scores </ h1 >
< div class =" score-card-list " >
< mat-card class =" score-card " *ngFor =" let fixture of gameweek.fixtures " >
< span class =" home " > {{ fixture.homeTeamName }} </ span >
< span class =" score " > {{ fixture.result.goalsHomeTeam }} : {{ fixture.result.goalsAwayTeam }} </ span >
< span class =" away " > {{ fixture.awayTeamName }} </ span >
</ mat-card >
</ div >
</ div >
gameweek?.fixtures
syntax:the ?
シンボルはif (gameweek != null) { return gameweek.fixtures } else { return null }
の短い手として機能します。また、非同期の休憩コールによってのみ居住する変数にアクセスする場合は信じられないほど便利です。
et voila!
この次の部分は厳密に必要ではありませんが、物事を行うための重要な角張った方法を示しており、それを前進させることにした場合、コードをモジュラーで封じ込めることができるようになります(NativeScriptを、Aを作成する方法として見てくださいAngularとTypeScriptを備えたネイティブモバイルアプリ)。
フィクスチャスコアカードを独自のコンポーネントに吸収します。 CLI: ng generate component score-card
(またはng gc score-card
)のヘルプから始めます。これにより、 src/app/score-card
に.ts
、 .html
、および.css
ファイルが作成されます。
おなじみのデコレーターに迎えられるscore-card.component.ts
開きます。
...
@ Component ( {
selector : 'app-score-card' , // Note this!
templateUrl : './score-card.component.html' ,
styleUrls : [ './score-card.component.css' ]
} )
...
selector
フィールドに注意 - コンポーネントにアクセスする方法を教えてくれます(この場合、 <app-score-card></app-score-card>
タグを使用して)。
肉をapp.component.html
からscore-card.component.html
に移動することにより、コードをリファクタリングします:
<!-- app.component.html -->
< div class =" container " >
< h1 > Live football scores </ h1 >
< div class =" score-card-list " >
< app-score-card *ngFor =" let fixture of gameweek?.fixtures " [fixture] =" fixture " > </ app-score-card >
</ div >
</ div >
<!-- score-card.component.html -->
< mat-card class =" score-card " >
< span class =" home " > {{ fixture.homeTeamName }} </ span >
< span class =" score " >
{{ fixture.result.goalsHomeTeam }} : {{ fixture.result.goalsAwayTeam }}
</ span >
< span class =" away " > {{ fixture.awayTeamName }} </ span >
</ mat-card >
注[fixture]="fixture"
ビット<app-score-card>
タグの内側。これが、コンポーネント間で情報を渡す方法です。
角度構文では、 [...]
入力を示し、 (…)
出力を示し、 [(…)]
双方向の結合を示します。 [(…)]
は「バナナボックス構文」とも呼ばれ、 [(ngModel)]="someVariable"
の形で頻繁に遭遇します。これは、変数の値とDOMオブジェクトの値の間の双方向結合を意味します。これは、Angularの使用の重要な部分です。
たとえば、 input
タグの値を画面に表示される変数に直接マッピングでき、 input
要素の値が変更されるたびにDOMが自動的に更新されます。
< p >
What is your name?
< input type =" text " [(ngModel)] =" name " />
</ p >
< p >
Your name: {{ name }}
</ p >
ここでプランカーの例を確認できます。
サッカーに戻る:コンポーネントに入力値を受信するには、次のようにscore-card.component.ts
更新する必要もあります。
import { Component , Input } from '@angular/core' ;
import { Fixture } from '../models/fixture' ;
@ Component ( {
selector : 'app-score-card' ,
templateUrl : './score-card.component.html' ,
styleUrls : [ './score-card.component.css' ]
} )
export class ScoreCardComponent {
@ Input ( ) fixture : Fixture ; // Note the decorator
constructor ( ) { }
}
コンポーネント間でデータを渡すには、 @Input
/ @Output
の使用とサービスを使用するという2つの明白な方法があります。
@Input()
最初の方法は、親と子供の間でデータを渡すのに役立ちますが、データがネストされた層を通過する必要がある場合は退屈です。 @Input
デコレータを使用して、データを子コンポーネントに渡すことができます。この入力変数は、それがオブジェクトである場合は参照によって渡されるか、プリミティブの場合は値で渡されます。
// someComponent.ts
// ...
import { Input } from '@angular/core'
// ...
export class SomeComponent {
// @Input() variables get set after the `constructor(...)`
// method, but before `ngOnInit()` fires
@ Input ( ) aNumber : number ; // This gets set via parent HTML
// ...
}
<!-- someComponentParent.html -->
< h1 >
I am a parent where SomeComponent gets rendered
</ h1 >
<!-- Pass a value to the aNumber variable -->
< some-component [aNumber] =" 48 " > </ some-component >
@Output()
データは、 @Output()
デコレーターを使用して、子供から親コンポーネントに放出することもできます。これは方向性の結合ではなく、事前に定義された時代に発火するイベントエミッタです。典型的なユースケースは、子供の値が変更されたときに親に通知することです。
// someComponent.ts
// ...
import { Input , Output , EventEmitter } from '@angular/core'
// ...
export class SomeComponent {
@ Input ( ) aNumber : number ;
// Emits an event (of type `number`) to the parent
@ Output ( ) numberChanged : EventEmitter < number > = new EventEmitter < number > ( ) ;
// ...
// Event emitters need to be triggered manually
// Any object can be emitted
emitValueChanged ( ) : void {
this . numberChanged . emit ( this . aNumber ) ;
}
}
<!-- someComponentParent.html -->
< h1 >
I am a parent where SomeComponent gets rendered
</ h1 >
<!-- Pass a value to the aNumber variable -->
< some-component [aNumber] =" 48 " (valueChanged) =" aNumberChanged($event) " > </ some-component >
// someComponentParent.ts
export class SomeParentComponent {
// ...
aNumberChanged ( updatedNumber : number ) : void {
console . log ( `aNumber changed to ${ updatedNumber } ` ) ;
}
// ...
}
サービスの使用
コンポーネント間でデータを渡すためにサービスを使用する方法はたくさんあります。詳細はこのチュートリアルの範囲を超えていますが、比較的複雑なアプリケーションではおそらく必要になるため、そのような手法の存在に注目する価値があります。
一般的なパターンは、コンポーネントがメッセージをプッシュできる、または新しいメッセージの通知を購読することができるサービス内のObservable
ストリームを定義することです。これは事実上イベントバスです。
一般的なケースでは、リスナーが適用可能なものを識別できるようにするために、メッセージを入力する必要があります。やや軽薄な例として、このサービスは、 PersonComponent
が反応する可能性のあるPersonNameChangedEvent
を放出する可能性がありますが、 LandingPageComponent
このイベントを完全に無視することを選択できます。
Angularは、アプリケーションを構築するために、内部でモノリシックで高度に意見の高いフレームワークを提供します。これは構造の利点を提供します - フレームワークの意見のある性質は、ユーザーがGet -goからスケーラブルなアプリを設計するのに役立ち、多くの複雑さが開発者から隠されています。一方、Angular(およびTypeScript、そのため)を使用して、小さなアプリケーションを構築すると遅くなる可能性のある多くのボイラープレートコードを導入するため、Angularにコミットする前にアプリがどこに向かっているかを検討する価値があります。
しかし、Angular CLIは長い道のりを歩んできましたが、それがどれだけ重いものを持ち上げているかを考えると、近い将来、ほぼすべてのプロジェクトにAngularを使用するでしょう。