すべての特典、手間なし:Angular9チュートリアル

公開: 2022-03-11

「毎年インターネットは壊れています」ということわざがあり、開発者は通常それを修正する必要があります。 待望のAngularバージョン9では、これが当てはまると思うかもしれません。以前のバージョンで開発されたアプリは、主要な移行プロセスを経る必要があります。

しかし、そうではありません! Angularチームはコンパイラーを完全に再設計し、ビルドの高速化、テストの実行の高速化、バンドルサイズの縮小、そして最も重要なこととして、古いバージョンとの下位互換性を実現しました。 Angular 9を使用すると、開発者は基本的にすべての特典を手間をかけずに取得できます。

このAngular9チュートリアルでは、Angularアプリケーションを最初から作成します。 最新のAngular9機能のいくつかを使用し、その過程で他の改善点を検討します。

Angular 9チュートリアル:新しいAngularアプリケーションから始める

Angularプロジェクトの例から始めましょう。 まず、AngularのCLIの最新バージョンをインストールしましょう。

 npm install -g @angular/cli

ng versionを実行することで、AngularCLIのバージョンを確認できます。

次に、Angularアプリケーションを作成しましょう。

 ng new ng9-app --create-application=false --strict

ng newコマンドで2つの引数を使用しています。

  • --create-application=falseは、ワークスペースファイルのみを生成するようにCLIに指示します。 これにより、複数のアプリと複数のライブラリが必要な場合に、コードをより適切に整理できます。
  • --strictは、より厳密なルールを追加して、TypeScriptの入力とコードのクリーンさを強化します。

この結果、基本的なワークスペースフォルダーとファイルが作成されます。

node_modules、.editorconfig、.gitignore、angular.json、package-lock.json、package.json、README.md、tsconfig.json、およびtslint.jsonを含むng9-appフォルダーを示すIDEのスクリーンショット。

それでは、新しいアプリを追加しましょう。 これを行うには、次を実行します。

 ng generate application tv-show-rating

プロンプトが表示されます:

 ? Would you like to share anonymous usage data about this project with the Angular Team at Google under Google's Privacy Policy at https://policies.google.com/privacy? For more details and how to change this setting, see http://angular.io/analytics. No ? Would you like to add Angular routing? Yes ? Which stylesheet format would you like to use? SCSS

ここで、 ng serveを実行すると、アプリが最初のスキャフォールディングで実行されていることがわかります。

Angular 9の足場のスクリーンショット。「テレビ番組評価アプリが実行されています!」という通知が表示されます。リソースと次のステップへのリンクもあります。

ng build --prodを実行すると、生成されたファイルのリストが表示されます。

Angular9の「ngbuild--prod」出力のスクリーンショット。 「差分ロード用のES5バンドルの生成...」から始まります。その後、いくつかのJavaScriptファイルチャンク(ランタイム、ポリフィル、メイン、それぞれ-es2015および-es5バージョン)と1つのCSSファイルが一覧表示されます。最後の行は、タイムスタンプ、ハッシュ、および23,881ミリ秒の実行時間を示します。

各ファイルには2つのバージョンがあります。 1つはレガシーブラウザと互換性があり、もう1つはES2015をターゲットにコンパイルされています。これは、新しいAPIを使用し、ブラウザで実行するために必要なポリフィルが少なくて済みます。

Angular 9の大きな改善点の1つは、バンドルサイズです。 Angularチームによると、大きなアプリでは最大40%の減少が見られます。

新しく作成されたアプリの場合、バンドルサイズはAngular 8のサイズと非常に似ていますが、アプリが大きくなるにつれて、バンドルサイズが以前のバージョンよりも小さくなることがわかります。

Angular 9で導入されたもう1つの機能は、コンポーネントスタイルのCSSファイルのいずれかが定義されたしきい値よりも大きい場合に警告する機能です。

配列内に2つのオブジェクトがある、Angular9JSON構成ファイルの「予算」セクションのスクリーンショット。最初のオブジェクトでは、「type」が「initial」に設定され、「maximumWarning」が「2mb」に設定され、「maximumError」が「5mb」に設定されています。 2番目のオブジェクトでは、「type」が「anyComponentStyle」に設定され、「maximumWarning」が「6kb」に設定され、「maximumError」が「10kb」に設定されています。

これは、悪いスタイルのインポートや巨大なコンポーネントスタイルのファイルを見つけるのに役立ちます。

テレビ番組を評価するためのフォームの追加

次に、テレビ番組を評価するためのフォームを追加します。 そのために、最初に、 bootstrapng-bootstrapをインストールします。

 npm install bootstrap @ng-bootstrap/ng-bootstrap

Angular 9のもう1つの改善点は、i18n(国際化)です。 以前は、開発者はアプリ内のすべてのロケールに対してフルビルドを実行する必要がありました。 代わりに、Angular 9を使用すると、アプリを1回ビルドし、ビルド後のプロセスですべてのi18nファイルを生成できるため、ビルド時間が大幅に短縮されます。 ng-bootstrapはi18nに依存しているため、プロジェクトに新しいパッケージを追加します。

 ng add @angular/localize

次に、Bootstrapテーマをアプリのstyles.scssに追加します。

 @import "~bootstrap/scss/bootstrap";

そして、app.module.tsのAppModuleNgbModuleReactiveFormsModuleapp.module.tsます。

 // ... import { ReactiveFormsModule } from '@angular/forms'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; @NgModule({ imports: [ // ... ReactiveFormsModule, NgbModule ], })

次に、 app.component.htmlをフォームの基本グリッドで更新します。

 <div class="container"> <div class="row"> <div class="col-6"> </div> </div> </div>

そして、フォームコンポーネントを生成します。

 ng gc TvRatingForm

tv-rating-form.component.htmlを更新し、フォームを追加してテレビ番組を評価しましょう。

 <form [formGroup]="form" (ngSubmit)="submit()" class="mt-3"> <div class="form-group"> <label>TV SHOW</label> <select class="custom-select" formControlName="tvShow"> <option *ngFor="let tvShow of tvShows" [value]="tvShow.name">{{tvShow.name}}</option> </select> </div> <div class="form-group"> <ngb-rating [max]="5" formControlName="rating"></ngb-rating> </div> <button [disabled]="form.invalid || form.disabled" class="btn btn-primary">OK</button> </form>

そして、 tv-rating-form.component.tsは次のようになります。

 // ... export class TvRatingFormComponent implements OnInit { tvShows = [ { name: 'Better call Saul!' }, { name: 'Breaking Bad' }, { name: 'Lost' }, { name: 'Mad men' } ]; form = new FormGroup({ tvShow: new FormControl('', Validators.required), rating: new FormControl('', Validators.required), }); submit() { alert(JSON.stringify(this.form.value)); this.form.reset(); } }

最後に、フォームをapp.component.htmlに追加しましょう。

 <!-- ... --> <div class="col-6"> <app-tv-rating-form></app-tv-rating-form> </div>

この時点で、いくつかの基本的なUI機能があります。 ここで、 ng serveを再度実行すると、実際に動作していることがわかります。

Angular 9チュートリアルアプリのスクリーンショット。「TVSHOW」というタイトルのフォームが表示され、ドロップダウンにいくつかの番組タイトル、スターメーター、[OK]ボタンが表示されます。アニメーションでは、ユーザーは番組を選択し、評価を選択してから、[OK]ボタンをクリックします。

先に進む前に、デバッグに役立つように追加されたいくつかの興味深い新しいAngular9機能を簡単に見てみましょう。 これは私たちの日常業務で非常に一般的なタスクであるため、私たちの生活を少し楽にするために何が変わったかを知ることは価値があります。

Angular9Ivyを使用したデバッグ

Angular9とAngularIvyで導入されたもう1つの大きな改善点は、デバッグエクスペリエンスです。 コンパイラーは、より多くのエラーを検出し、より「読みやすい」方法でそれらをスローできるようになりました。

実際の動作を見てみましょう。 まず、 tsconfig.jsonでテンプレートチェックをアクティブにします。

 { // ... "angularCompilerOptions": { "fullTemplateTypeCheck": true, "strictInjectionParameters": true, "strictTemplates": true } }

ここで、 tvShows配列を更新し、 nametitleに変更すると、次のようになります。

 tvShows = [ { title: 'Better call Saul!' }, { title: 'Breaking Bad' }, { title: 'Lost' }, { title: 'Mad men' } ];

…コンパイラからエラーが発生します。

Angular 9 / Angular Ivyコンパイラ出力のスクリーンショット。ファイル名と位置が示され、「エラーTS2339:プロパティ'name'はタイプ'{title:string;}'に存在しません。」と表示されます。また、問題のコード行を示し、参照に下線を付けます。この場合は、tvShow.nameが言及されているtv-rating-form.component.htmlファイルにあります。その後、このHTMLファイルへの参照は、対応するTypeScriptファイルまでトレースされ、同様に強調表示されます。

このタイプチェックにより、タイプミスやTypeScriptタイプの誤った使用を防ぐことができます。

@Input()のAngularIvy検証

私たちが得るもう1つの良い検証は、 @Input()を使用することです。 たとえば、これをtv-rating-form.component.tsに追加できます。

 @Input() title: string;

…そしてそれをapp.component.htmlにバインドします:

 <app-tv-rating-form [title]="title"></app-tv-rating-form>

…次に、 app.component.tsを次のように変更します。

 // ... export class AppComponent { title = null; }

これらの3つの変更を行うと、コンパイラから別のタイプのエラーが発生します。

Angular 9 / Angular Ivyコンパイラ出力のスクリーンショット。前の形式と同様の形式で、app.component.htmlに「エラーTS 2322:タイプ'null'はタイプ'string'に割り当てられません」と強調表示されています。

それをバイパスしたい場合は、テンプレートで$any()使用して値をanyにキャストし、エラーを修正できます。

 <app-tv-rating-form [title]="$any(title)"></app-tv-rating-form>

ただし、これを修正する正しい方法は、フォームのtitleをnull許容にすることです。

 @Input() title: string | null ;

Angular9IvyのExpressionChangedAfterItHasBeenCheckedError

Angular開発で最も恐ろしいエラーの1つは、 ExpressionChangedAfterItHasBeenCheckedErrorです。 ありがたいことに、Ivyはエラーをより明確に出力し、問題の原因を簡単に見つけられるようにします。

それでは、 ExpressionChangedAfterItHasBeenCheckedErrorエラーを導入しましょう。 そのためには、まず、サービスを生成します。

 ng gs Title

次に、 BehaviorSubjectと、 Observableにアクセスして新しい値を発行するメソッドを追加します。

 export class TitleService { private bs = new BehaviorSubject < string > (''); constructor() {} get title$() { return this.bs.asObservable(); } update(title: string) { this.bs.next(title); } }

その後、これをapp.component.htmlに追加します。

 <!-- ... --> <div class="col-6"> <h2> {{title$ | async}} </h2> <app-tv-rating-form [title]="title"></app-tv-rating-form> </div>

そして、 app.component.tsに、 TitleServiceを挿入します。

 export class AppComponent implements OnInit { // ... title$: Observable < string > ; constructor( private titleSvc: TitleService ) {} ngOnInit() { this.title$ = this.titleSvc.title$; } // ... }

最後に、 tv-rating-form.component.tsで、 TitleServiceを挿入し、 AppComponentのタイトルを更新します。これにより、 ExpressionChangedAfterItHasBeenCheckedErrorエラーがスローされます。

 // ... constructor( private titleSvc: TitleService ) { } ngOnInit() { this.titleSvc.update('new title!'); }

これで、ブラウザの開発コンソールで詳細なエラーを確認できますapp.component.htmlをクリックすると、エラーの場所が示されます。

ブラウザの開発コンソールのスクリーンショット。AngularIvyによるExpressionChangedAfterItHasBeenCheckedErrorエラーの報告を示しています。赤いテキストのスタックトレースは、以前の値と現在の値、およびヒントとともにエラーを示します。スタックトレースの真ん中にあるのは、core.jsを参照していない唯一の行です。ユーザーがそれをクリックすると、エラーの原因となっているapp.component.htmlの行が表示されます。

このエラーは、サービス呼び出しをsetTimeoutでラップすることで修正できます。

 setTimeout(() => { this.titleSvc.update('new title!'); });

ExpressionChangedAfterItHasBeenCheckedErrorエラーが発生する理由を理解し、他の可能性を探るには、このトピックに関するMaximKoretskyiの投稿を読む価値があります。

Angular Ivyを使用すると、エラーをより明確に表示でき、コードにTypeScriptの入力を強制できます。 次のセクションでは、Ivyとデバッグを利用するいくつかの一般的なシナリオについて説明します。

コンポーネントハーネスを使用したAngular9アプリのテストの作成

Angular 9では、コンポーネントハーネスと呼ばれる新しいテストAPIが導入されました。 その背後にある考え方は、DOMとの対話に必要なすべての雑用を取り除き、操作をはるかに簡単にし、実行をより安定させることです。

コンポーネントハーネスAPIは@angular/cdkライブラリに含まれているので、最初にそれをプロジェクトにインストールしましょう。

 npm install @angular/cdk

これで、テストを作成してコンポーネントハーネスを活用できます。 tv-rating-form.component.spec.tsで、テストを設定しましょう。

 import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { ReactiveFormsModule } from '@angular/forms'; describe('TvRatingFormComponent', () => { let component: TvRatingFormComponent; let fixture: ComponentFixture < TvRatingFormComponent > ; beforeEach(async (() => { TestBed.configureTestingModule({ imports: [ NgbModule, ReactiveFormsModule ], declarations: [TvRatingFormComponent] }).compileComponents(); })); // ... });

次に、コンポーネントにComponentHarnessを実装しましょう。 2つのハーネスを作成します。1つはTvRatingForm用で、もう1つはNgbRating用です。 ComponentHarnessには、コンポーネントのセレクターの値をとるstaticフィールドhostSelectorが必要です。

 // ... import { ComponentHarness, HarnessLoader } from '@angular/cdk/testing'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; class TvRatingFormHarness extends ComponentHarness { static hostSelector = 'app-tv-rating-form'; } class NgbRatingHarness extends ComponentHarness { static hostSelector = 'ngb-rating'; } // ...

TvRatingFormHarnessの場合、送信ボタンのセレクターとclickをトリガーする関数を作成します。 これを実装するのがどれほど簡単になるかがわかります。

 class TvRatingFormHarness extends ComponentHarness { // ... protected getButton = this.locatorFor('button'); async submit() { const button = await this.getButton(); await button.click(); } }

次に、評価を設定するメソッドを追加します。 ここでは、 locatorForAllを使用して、ユーザーがクリックできる星を表すすべての<span>要素を検索します。 rate関数は、可能なすべての評価の星を取得し、送信された値に対応する星をクリックするだけです。

 class NgbRatingHarness extends ComponentHarness { // ... protected getRatings = this.locatorForAll('span:not(.sr-only)'); async rate(value: number) { const ratings = await this.getRatings(); return ratings[value - 1].click(); } }

最後に欠けているのは、 TvRatingFormHarnessNgbRatingHarnessに接続することです。 これを行うには、 TvRatingFormHarnessクラスにロケーターを追加するだけです。

 class TvRatingFormHarness extends ComponentHarness { // ... getRating = this.locatorFor(NgbRatingHarness); // ... }

それでは、テストを書いてみましょう。

 describe('TvRatingFormComponent', () => { // ... it('should pop an alert on submit', async () => { spyOn(window, 'alert'); const select = fixture.debugElement.query(By.css('select')).nativeElement; select.value = 'Lost'; select.dispatchEvent(new Event('change')); fixture.detectChanges(); const harness = await TestbedHarnessEnvironment.harnessForFixture(fixture, TvRatingFormHarness); const rating = await harness.getRating(); await rating.rate(1); await harness.submit(); expect(window.alert).toHaveBeenCalledWith('{"tvShow":"Lost","rating":1}'); }); });

フォーム内のselectでは、ハーネスを介した値の設定を実装していないことに注意してください。 これは、APIがまだオプションの選択をサポートしていないためです。 しかし、これにより、コンポーネントが利用される前の要素との相互作用がどのようになっていたかをここで比較する機会が得られます。

テストを実行する前に最後にもう1つ。 titlenullに更新したため、 app.component.spec.tsを修正する必要があります。

 describe('AppComponent', () => { // ... it(`should have as title 'tv-show-rating'`, () => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.componentInstance; expect(app.title).toEqual(null); }); });

ここで、 ng testを実行すると、テストに合格します。

Angular9アプリでテストを実行しているKarmaのスクリーンショット。 「不完全:fit()またはfdescribe()が見つかりました、2つの仕様、0の失敗、シード69573でランダム化されました」というメッセージとともに、「Ran 2of6specs」が表示されます。 TvRatingFormComponentの2つのテストが強調表示されています。 AppComponentの3つのテストとTitleServiceの1つのテストはすべて灰色です。

Angular 9サンプルアプリに戻る:データベースへのデータの保存

Firestoreへの接続を追加し、データベースに評価を保存して、Angular9チュートリアルを締めくくりましょう。

そのためには、Firebaseプロジェクトを作成する必要があります。 次に、必要な依存関係をインストールします。

 npm install @angular/fire firebase

Firebase Consoleのプロジェクト設定で、その構成を取得し、 environment.tsenvironment.prod.tsに追加します。

 export const environment = { // ... firebase: { apiKey: '{your-api-key}', authDomain: '{your-project-id}.firebaseapp.com', databaseURL: 'https://{your-project-id}.firebaseio.com', projectId: '{your-project-id}', storageBucket: '{your-project-id}.appspot.com', messagingSenderId: '{your-messaging-id}', appId: '{your-app-id}' } };

その後、必要なモジュールをapp.module.tsにインポートします。

 import { AngularFireModule } from '@angular/fire'; import { AngularFirestoreModule } from '@angular/fire/firestore'; import { environment } from '../environments/environment'; @NgModule({ // ... imports: [ // ... AngularFireModule.initializeApp(environment.firebase), AngularFirestoreModule, ], // ... })

次に、 tv-rating-form.component.tsで、 AngularFirestoreサービスを挿入し、フォーム送信時に新しい評価を保存します。

 import { AngularFirestore } from '@angular/fire/firestore'; export class TvRatingFormComponent implements OnInit { constructor( // ... private af: AngularFirestore, ) { } async submit(event: any) { this.form.disable(); await this.af.collection('ratings').add(this.form.value); this.form.enable(); this.form.reset(); } } 

大きなページタイトル「newtitle!」の下に「TVSHOW」というタイトルのフォームを表示するAngular9チュートリアルアプリのスクリーンショット。ここでも、いくつかの番組タイトル、スターメーター、[OK]ボタンを一覧表示するドロップダウンがあり、ユーザーは番組を選択して評価を選択し、[OK]ボタンをクリックします。

これで、Firebase Consoleに移動すると、新しく作成されたアイテムが表示されます。

Firebaseコンソールのスクリーンショット。左側の列には、参加者、レース、評価、テスト、ユーザーなどのコレクションを含むjoaq-labがあります。評価項目が選択され、IDが選択された中央の列に表示されます。これが唯一のドキュメントです。右の列には2つのフィールドが表示されます。「rating」は4に設定され、「tvShow」は「Madmen」に設定されています。

最後に、すべての評価をAppComponentにリストします。 これを行うには、 app.component.tsで、コレクションからデータを取得します。

 import { AngularFirestore } from '@angular/fire/firestore'; export class AppComponent implements OnInit { // ... ratings$: Observable<any>; constructor( // ... private af: AngularFirestore ) { } ngOnInit() { // ... this.ratings$ = this.af.collection('ratings').valueChanges(); } }

…そしてapp.component.htmlに、評価のリストを追加します。

 <div class="container"> <div class="row"> // ... <div class="col-6"> <div> <p *ngFor="let rating of ratings$ | async"> {{rating.tvShow}} ({{rating.rating}}) </p> </div> </div> </div> </div>

これは、Angular9チュートリアルアプリをすべてまとめたときの外観です。

大きなページタイトル「newtitle!」の下に「TVSHOW」というタイトルのフォームを表示するAngular9チュートリアルアプリのスクリーンショット。繰り返しになりますが、いくつかの番組タイトルを一覧表示するドロップダウン、スターメーター、および[OK]ボタンがあります。今回は、右側の列にすでに「マッドメン(4)」がリストされており、ユーザーは「ロスト」を3つ星で評価し、続いて「マッドメン」を4つ星で評価しています。右側の列は、両方の新しい評価の後、アルファベット順に並べられたままです。

Angular9とAngularIvy:より良い開発、より良いアプリ、そしてより良い互換性

このAngular9チュートリアルでは、基本的なフォームの作成、Firebaseへのデータの保存、Firebaseからのアイテムの取得について説明しました。

その過程で、Angular9とAngularIvyにどの改善と新機能が含まれているかを確認しました。 完全なリストについては、Angularの公式ブログの最新リリース投稿を確認してください。


GoogleCloudPartnerバッジ。

Google Cloudパートナーとして、ToptalのGoogle認定エキスパートは、最も重要なプロジェクトのオンデマンドで企業に提供されます。