序章

Angularで時間を過ごしたことがあれば、データや機能を共有したいと思っていて、サービス/プロバイダーを使用したことがあるかもしれません。

共通のデータ機能の代わりに、共通のUI機能が必要な場合はどうなりますか? たとえば、ボタンを使用して1つのコンポーネントから別のコンポーネントに移動する単純なシナリオを考えてみます。 これを実装する簡単な方法は、ボタンを作成し、コード内のメソッドを呼び出してから、Angularルーターを使用してページに移動することです。 また、各コンポーネントで同じコードを繰り返したくない場合はどうでしょうか。 TypescriptとAngularは、このカプセル化を処理する方法を提供します。 継承されたコンポーネント!

TypeScriptでクラス継承を使用すると、一般的なUI機能を含む基本コンポーネントを宣言し、それを使用して必要な標準コンポーネントを拡張できます。 C#などのオブジェクト指向の方法論に焦点を当てた言語に慣れている場合は、このアプローチを継承として非常に簡単に認識できます。 Typescriptでは、単にクラスの拡張として知られています。

この記事では、一般的なコードを基本コンポーネントにカプセル化し、それを拡張して再利用可能なページナビゲーションボタンを作成します。

前提条件

このチュートリアルを完了するには、次のものが必要です。

このチュートリアルは、ノードv16.4.2、npm v7.19.1、および@angular/corev12.1.1で検証されました。

ステップ1-プロジェクトの設定

AngularCLIを使用して新しいアプリを作成することから始めましょう。

以前にAngularCLIをインストールしたことがない場合は、npmを使用してグローバルにインストールします。

  1. npm install -g @angular/cli

次に、CLIを使用して新しいアプリを作成します。

  1. ng new AngularComponentInheritance --style=css --routing --skip-tests

注: ng newコマンドにフラグを渡して、アプリにルーティングを追加し(--routing)、テストファイルを追加しません(--skip-tests)。 )。

プロジェクトディレクトリに移動します。

  1. cd AngularComponentInheritance

次に、次のコマンドを実行して、Baseコンポーネントを作成します。

  1. ng generate component base --inline-template --inline-style --skip-tests --module app

注:ここでの--moduleフラグは、コンポーネントが属するモジュールを指定します。

このコマンドは、base.component.tsファイルを作成し、appモジュールのdeclarationとして追加します。

ステップ2–ベースコンポーネントの構築

コードエディタでbase/base.component.tsファイルを開きます。

src / app / base / base.component.ts
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-base',
  template: `
    <p>
      base works!
    </p>
  `,
  styles: [
  ]
})
export class BaseComponent implements OnInit {

  constructor() { }

  ngOnInit(): void {
  }

}

このUIは表示されないため、単純なUI以外のものを追加する必要はありません。

次に、Routerをコンポーネントに挿入します。

src / app / base / base.component.ts
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';

@Component({
  selector: 'app-base',
  template: `
    <p>
      base works!
    </p>
  `,
  styles: [
  ]
})
export class BaseComponent implements OnInit {

  constructor(public router: Router) { }

  ngOnInit(): void {
  }

}

アクセシビリティレベルに注意してください。 継承のため、この宣言“public”を保持することが重要です。

次に、openPageというメソッドをベースコンポーネントに追加します。このメソッドは、文字列を受け取り、それを使用してルートに移動します(注:テンプレートリテラルの単一引用符の代わりに、以下のチェックマークを使用してください)::

src / app / base / base.component.ts
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';

@Component({
  selector: 'app-base',
  template: `
    <p>
      base works!
    </p>
  `,
  styles: [
  ]
})
export class BaseComponent implements OnInit {

  constructor(public router: Router) { }

  ngOnInit(): void {
  }

  openPage(routename: string) {
    this.router.navigateByUrl(`/${routename}`);
  }
}

これにより、必要な基本機能が得られるので、いくつかのコンポーネントで使用してみましょう。

ステップ3–コンポーネントの継承

3つのAngularCLIコマンドを実行して、さらにいくつかのコンポーネントを生成します。

  1. ng generate component pageone --skip-tests --module app
  2. ng generate component pagetwo --skip-tests --module app
  3. ng generate component pagethree --skip-tests --module app

これらのコマンドは、PageoneComponentPagetwoComponent、およびPagethreeComponentを生成し、appdelcarationsとして3つすべてを追加します。

アプリを最初に生成したときにCLIによって作成されたapp-routing.module.tsを開き、各ページのパスを追加します。

src / app / app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { PageoneComponent } from './pageone/pageone.component';
import { PagetwoComponent } from './pagetwo/pagetwo.component';
import { PagethreeComponent } from './pagethree/pagethree.component';

const routes: Routes = [
  { path: '', component: PageoneComponent },
  { path: 'pageone', component: PageoneComponent },
  { path: 'pagetwo', component: PagetwoComponent },
  { path: 'pagethree', component: PagethreeComponent }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

ページコンポーネントを開き、extendsBaseComponentにします。

src / app / pageone / pageone.component.ts
import { Component, OnInit } from '@angular/core';
import { BaseComponent } from '../base/base.component';

@Component({
  selector: 'app-pageone',
  templateUrl: './pageone.component.html',
  styleUrls: ['./pageone.component.css']
})
export class PageoneComponent extends BaseComponent implements OnInit {

  constructor() { }

  ngOnInit(): void {
  }

}

ルーターを追加し、superを使用してBaseComponentコンストラクターに挿入するだけでなく、次のようになります。

src / app / pageone / pageone.component.ts
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { BaseComponent } from '../base/base.component';

@Component({
  selector: 'app-pageone',
  templateUrl: './pageone.component.html',
  styleUrls: ['./pageone.component.css']
})
export class PageoneComponent extends BaseComponent implements OnInit {

  constructor(public router: Router) {
    super(router);
  }

  ngOnInit(): void {
  }

}

これにより、挿入されたルーターモジュールが取得され、拡張コンポーネントに渡されます。

次に、残りのページコンポーネントに対してこれらの変更を繰り返します。

基本コンポーネントから継承が渡されるため、基本コンポーネントで定義されたものはすべて、それを拡張するすべてのコンポーネントで使用できます。 それでは、基本機能を使用してみましょう。

pageone.component.htmlテンプレートに2つのボタンを追加しましょう。

src / app / pageone / pageone.component.html
<button type="button" (click)="openPage('pagetwo')">
  Page Two
</button>

<button type="button" (click)="openPage('pagethree')">
  Page Three
</button>

openPageメソッドを使用するために追加の資格が必要ないことに注意してください。 C#のような言語で同様の継承を行う方法を考えると、base.openPage()のようなものを呼び出すことになります。 TypeScriptでこれを行う必要がない理由は、トランスパイル中に発生する魔法のためです。 TypeScriptはコードをJavaScriptに変換し、baseコンポーネントモジュールはpageoneコンポーネントにインポートされるため、コンポーネントで直接使用できます。

トランスパイルされたJavaScriptを見ると、これが少し明確になります。

var PageoneComponent = /** @class */ (function (_super) {
    __extends(PageoneComponent, _super);
    function PageoneComponent(router) {
        var _this = _super.call(this, router) || this;
        _this.router = router;
        return _this;
    }
    PageoneComponent.prototype.ngOnInit = function () {
    };
    PageoneComponent = __decorate([
        Object(_angular_core__WEBPACK_IMPORTED_MODULE_0__["Component"])({
            selector: 'app-pageone',
            template:
             __webpack_require__("./src/app/pageone/pageone.component.html"),
            styles: [
              __webpack_require__("./src/app/pageone/pageone.component.css")
            ]
        }),
        __metadata("design:paramtypes",
            [_angular_router__WEBPACK_IMPORTED_MODULE_2__["Router"]])
    ], PageoneComponent);
    return PageoneComponent;
}(_base_base_component__WEBPACK_IMPORTED_MODULE_1__["BaseComponent"]));

これが、注入されたモジュールをprivateではなくpublicのままにする必要がある理由でもあります。 TypeScriptでは、super()は、baseコンポーネントのコンストラクターが呼び出される方法であり、挿入されたすべてのモジュールを渡す必要があります。 モジュールがprivateの場合、それらは別個の宣言になります。 それらをpublicのままにし、superを使用して渡します。これらは、単一の宣言のままです。

ステップ4–アプリの完成

コードエディタを使用して、app.component.htmlからボイラープレートコードを削除し、<router-outlet>のみを残します。

src / app / app.component.html
<router-outlet></router-outlet>

Pageoneが終了したら、CLIを使用してアプリを実行し、機能を調べてみましょう。

  1. ng serve

ボタンの1つをクリックして、期待されるページに移動することを確認します。

これは、単一のコンポーネントの機能をカプセル化するための多くのオーバーヘッドであるため、PagetwoコンポーネントとPagethreeコンポーネントの両方を拡張し、他のページに移動するためのボタンを追加しましょう。

まず、pagetwo.component.tsを開き、pageone.component.tsのように更新します。

src / app / pagetwo / pagetwo.component.ts
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { BaseComponent } from '../base/base.component';

@Component({
  selector: 'app-pagetwo',
  templateUrl: './pagetwo.component.html',
  styleUrls: ['./pagetwo.component.css']
})
export class PagetwoComponent extends BaseComponent implements OnInit {

  constructor(public router: Router) {
    super(router);
  }

  ngOnInit(): void {
  }

}

次に、pagetwo.component.htmlを開き、pageonepagethreeのボタンを追加します。

src / app / pagetwo / pagetwo.component.html
<button type="button" (click)="openPage('pageone')">
  Page One
</button>

<button type="button" (click)="openPage('pagethree')">
  Page Three
</button>

次に、pagethree.component.tsを開き、pageone.component.tsのように更新します。

src / app / pagethree / pagethree.component.ts
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { BaseComponent } from '../base/base.component';

@Component({
  selector: 'app-pagethree',
  templateUrl: './pagethree.component.html',
  styleUrls: ['./pagethree.component.css']
})
export class PagethreeComponent extends BaseComponent implements OnInit {

  constructor(public router: Router) {
    super(router);
  }

  ngOnInit(): void {
  }

}

次に、pagethree.component.htmlを開き、pageonepagetwoのボタンを追加します。

src / app / pagethree / pagethree.component.html
<button type="button" (click)="openPage('pageone')">
  Page One
</button>

<button type="button" (click)="openPage('pagetwo')">
  Page Two
</button>

これで、ロジックを繰り返すことなく、アプリ内を移動できます。

結論

この記事では、共通コードを基本コンポーネントにカプセル化し、それを拡張して再利用可能なページナビゲーションボタンを作成しました。

ここから、多くのコンポーネントに共通の機能を拡張する方法を簡単に確認できます。 ナビゲーション、一般的なモーダルアラートUI、またはその他の処理を行っているかどうかにかかわらず、TypeScriptを介して付与された継承モデルを使用してコンポーネントを拡張することは、ツールボックスに保持する強力なツールです。

このチュートリアルのコードは、GitHubで入手できます。