如何使用Angular组件继承扩展类

来自菜鸟教程
跳转至:导航、​搜索

介绍

如果您曾在 Angular 中使用过任何时间,您可能遇到过想要共享数据或功能并且使用 services/providers 的时间。

如果您想要通用 UI 功能而不是通用数据功能怎么办? 例如,考虑一个简单的场景,您希望使用按钮从一个组件导航到另一个组件。 实现这一点的一种简单方法是创建一个按钮,在代码中调用一个方法,然后使用 Angular 路由器导航到页面。 如果您不想在每个组件中重复相同的代码怎么办? Typescript 和 Angular 为您提供了一种处理这种封装的方法。 继承组件!

使用 TypeScript 中的类继承,您可以声明一个包含通用 UI 功能的基础组件,并使用它来扩展您想要的任何标准组件。 如果您习惯于任何专注于面向对象方法的语言(例如 C#),您会很容易地将这种方法识别为继承。 使用 Typescript,它仅仅被称为扩展一个类。

在本文中,您将把通用代码封装在一个基本组件中,并对其进行扩展以创建可重用的页面导航按钮。

先决条件

要完成本教程,您需要:

  • Node.js 安装在本地,您可以按照【X57X】如何安装Node.js 并创建本地开发环境【X126X】进行。
  • 熟悉 设置 Angular 项目

本教程已使用 Node v16.4.2、npm v7.19.1 和 @angular/core v12.1.1 进行了验证。

第 1 步 - 设置项目

让我们首先使用 Angular CLI 创建一个新应用程序。

如果您之前没有安装过 Angular CLI,请使用 npm 全局安装:

npm install -g @angular/cli

接下来,使用 CLI 创建新应用程序:

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

注意: 我们将一些标志传递给 ng new 命令以向我们的应用程序添加路由(--routing),而不是添加任何测试文件(--skip-tests )。


导航到项目目录:

cd AngularComponentInheritance

然后,运行以下命令创建一个 Base 组件:

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}`);
  }
}

这为我们提供了我们需要的基本功能,所以让我们在一些组件上使用它。

第三步——继承组件

我们将运行三个 Angular CLI 命令来生成更多组件:

ng generate component pageone --skip-tests --module app
ng generate component pagetwo --skip-tests --module app
ng generate component pagethree --skip-tests --module app

这些命令将生成 PageoneComponentPagetwoComponentPagethreeComponent 并将所有三个添加为 appdelcarations

打开app-routing.module.ts,这是我们第一次生成应用程序时由CLI创建的,并为每个页面添加一个路径:

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 模板中添加两个按钮:

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"]));

这也是为什么我们需要保留注入的模块 public 而不是 private。 在 TypeScript 中,super() 是调用 base 组件的构造函数的方式,需要传递所有注入的模块。 当模块是 private 时,它们成为单独的声明。 保留它们 public 并使用 super 传递它们,它们仍然是一个声明。

第 4 步 - 完成应用程序

花点时间使用您的代码编辑器从 app.component.html 中删除样板代码,只留下 <router-outlet>

src/app/app.component.html

<router-outlet></router-outlet>

完成 Pageone 后,让我们使用 CLI 运行应用程序并研究功能:

ng serve

单击其中一个按钮并观察您是否被定向到预期页面。

封装单个组件的功能需要大量开销,因此让我们扩展 PagetwoPagethree 组件以及添加按钮以帮助导航到其他页面。

首先,打开 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 上找到。