すべての特典、手間なし: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の入力とコードのクリーンさを強化します。
この結果、基本的なワークスペースフォルダーとファイルが作成されます。
それでは、新しいアプリを追加しましょう。 これを行うには、次を実行します。
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
を実行すると、アプリが最初のスキャフォールディングで実行されていることがわかります。
ng build --prod
を実行すると、生成されたファイルのリストが表示されます。
各ファイルには2つのバージョンがあります。 1つはレガシーブラウザと互換性があり、もう1つはES2015をターゲットにコンパイルされています。これは、新しいAPIを使用し、ブラウザで実行するために必要なポリフィルが少なくて済みます。
Angular 9の大きな改善点の1つは、バンドルサイズです。 Angularチームによると、大きなアプリでは最大40%の減少が見られます。
新しく作成されたアプリの場合、バンドルサイズはAngular 8のサイズと非常に似ていますが、アプリが大きくなるにつれて、バンドルサイズが以前のバージョンよりも小さくなることがわかります。
Angular 9で導入されたもう1つの機能は、コンポーネントスタイルのCSSファイルのいずれかが定義されたしきい値よりも大きい場合に警告する機能です。
これは、悪いスタイルのインポートや巨大なコンポーネントスタイルのファイルを見つけるのに役立ちます。
テレビ番組を評価するためのフォームの追加
次に、テレビ番組を評価するためのフォームを追加します。 そのために、最初に、 bootstrap
とng-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のAppModule
にNgbModule
とReactiveFormsModule
をapp.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
を再度実行すると、実際に動作していることがわかります。
先に進む前に、デバッグに役立つように追加されたいくつかの興味深い新しいAngular9機能を簡単に見てみましょう。 これは私たちの日常業務で非常に一般的なタスクであるため、私たちの生活を少し楽にするために何が変わったかを知ることは価値があります。
Angular9Ivyを使用したデバッグ
Angular9とAngularIvyで導入されたもう1つの大きな改善点は、デバッグエクスペリエンスです。 コンパイラーは、より多くのエラーを検出し、より「読みやすい」方法でそれらをスローできるようになりました。
実際の動作を見てみましょう。 まず、 tsconfig.json
でテンプレートチェックをアクティブにします。
{ // ... "angularCompilerOptions": { "fullTemplateTypeCheck": true, "strictInjectionParameters": true, "strictTemplates": true } }
ここで、 tvShows
配列を更新し、 name
をtitle
に変更すると、次のようになります。
tvShows = [ { title: 'Better call Saul!' }, { title: 'Breaking Bad' }, { title: 'Lost' }, { title: 'Mad men' } ];
…コンパイラからエラーが発生します。
このタイプチェックにより、タイプミスや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つの変更を行うと、コンパイラから別のタイプのエラーが発生します。
それをバイパスしたい場合は、テンプレートで$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
をクリックすると、エラーの場所が示されます。
このエラーは、サービス呼び出しを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(); } }
最後に欠けているのは、 TvRatingFormHarness
をNgbRatingHarness
に接続することです。 これを行うには、 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つ。 title
をnull
に更新したため、 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
を実行すると、テストに合格します。
Angular 9サンプルアプリに戻る:データベースへのデータの保存
Firestoreへの接続を追加し、データベースに評価を保存して、Angular9チュートリアルを締めくくりましょう。
そのためには、Firebaseプロジェクトを作成する必要があります。 次に、必要な依存関係をインストールします。
npm install @angular/fire firebase
Firebase Consoleのプロジェクト設定で、その構成を取得し、 environment.ts
とenvironment.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(); } }
これで、Firebase Consoleに移動すると、新しく作成されたアイテムが表示されます。
最後に、すべての評価を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チュートリアルアプリをすべてまとめたときの外観です。
Angular9とAngularIvy:より良い開発、より良いアプリ、そしてより良い互換性
このAngular9チュートリアルでは、基本的なフォームの作成、Firebaseへのデータの保存、Firebaseからのアイテムの取得について説明しました。
その過程で、Angular9とAngularIvyにどの改善と新機能が含まれているかを確認しました。 完全なリストについては、Angularの公式ブログの最新リリース投稿を確認してください。
Google Cloudパートナーとして、ToptalのGoogle認定エキスパートは、最も重要なプロジェクトのオンデマンドで企業に提供されます。