65.9K
CodeProject 正在变化。 阅读更多。
Home

在 Angular 中显示数据和处理事件

2018年8月14日

CPOL

25分钟阅读

viewsIcon

10464

downloadIcon

246

在这里,我们将学习不同的绑定方法,如类绑定、样式绑定和属性绑定。我们将了解如何在视图中显示数据并处理 DOM 中的任何事件。

引言

今天我们将学习如何在视图中显示数据,如何动态地将样式和类应用于 DOM 元素。我们将使用管道格式化数据,我们将构建自定义管道。我们将处理 DOM 元素引发的事件。但在开始之前,如果您没有 Angular 和 Typescript 构建块的先验知识,那么您应该从这里开始您的 Angular 之旅。

目录

因此,在本文中我们将涵盖很多内容。

属性绑定

我们之前已经学习过字符串插值是用于显示数据的双花括号语法。

@Component({
    selector: 'courses',
    template: `
    <h2> {{ name }} </h2>
    <img src="{{ image }}" />
    `
})

export class CoursesComponent {
    name = "Usama";
    image = "http://www.gstatic.com/webp/gallery/1.jpg";
}

现在,这种字符串插值只不过是一种语法糖,在幕后,当 Angular 编译模板时,它会将这些插值转换为属性绑定。

现在,让我们用属性绑定语法重写 img,而不是使用字符串插值。所以,

@Component({
    selector: 'courses',
    template: `
    <h2> {{ name }} </h2>                 // 1<sup>st</sup> Statement
    <img src="{{ image }}" />             // 2<sup>nd</sup> Statement
   
    <img [src]=”image” />                 // Compiled Version of 2<sup>nd</sup> Statement
    `
})

这就是它在底层编译字符串插值的方式。请注意,这里没有双花括号。因此,当我们使用字符串插值中的数据时,angular 会自动将其编译为 Angular 编译语法。

通过这种方式,每当图像字段值更改时,此图像的 **src** 属性都会自动更新。

现在您可能想知道是使用字符串插值还是 Angular 的原始语法???

插值非常适用于在任何我们使用文本的元素(如标题、div、section、text 等)之间添加动态值。在上面的示例中,如果我们将属性绑定语法与 h2 标题一起使用。我们需要记住如何使用属性绑定语法编写语句。

<h2 [textContent]=”name”></h2>

显然,这种语法更长、更嘈杂,因此我们建议在需要 HTML 元素中的文本时使用字符串插值。在其他情况下,例如 img,属性绑定语法更短。我们也可以在 img 情况下使用字符串插值,但属性绑定语法更简洁、更短。

请记住,属性绑定语法只以一种方式(组件到 DOM)工作。因此,如果我们在组件类属性中进行任何更改,它会自动更改 DOM,但 DOM 中的任何更改都不会反映回组件。

简单来说,如果我们在表单中填写字段,那么您输入的值不能更改组件属性的值,但是如果组件字段发生任何更改,DOM(表单输入字段)会自动相应地更新。

属性绑定

我们已经了解了属性绑定的概念,现在让我们探讨属性绑定的概念。现在我们的模板代码是,

@Component({
    selector: 'courses',
    template: `
        <table>
            <tr>
                <td [colspan]="colSpan"></td>
            </tr>
        </table>
    `
})

export class CoursesComponent {
    colSpan = 5;
}

现在保存文件并运行应用程序。在这里您将看到空白的白色屏幕,这意味着有些地方不对劲,并且可能存在一些错误,现在打开浏览器的控制台。

现在要理解这个错误,首先我们需要理解 DOM(文档对象模型)和 HTML 的区别。

DOM 与 HTML

DOM 是一个表示文档结构的对象模型,它本质上是内存中的一个对象树。

另一方面,HTML 是我们用来以文本形式表示 DOM 的标记语言。因此,当您的浏览器解析 HTML 文档时,它会在内存中创建一个对象树,我们将其称为 DOM(上图)。我们也可以使用 Vanilla Javascript 以编程方式创建这些对象树。我们不一定需要 HTML,但使用 HTML 要简单得多。

**重要一点** 是大多数 HTML 元素的属性与 DOM 对象的属性具有一对一的映射。

但是有一些例外,即我们有一些 HTML 属性在 DOM 中没有表示,例如 colspan。

这就是为什么当我们解析组件的这个模板 html 标记并为这个 **td** 创建一个实际的 DOM 对象时,DOM 对象没有一个名为 colspan 的属性,这就是我们得到这个错误的原因。所以,colspan 是 DOM 中 td 的未知属性。

此外,DOM 中有一些属性在 html 中没有表示,例如

<h1 [textContent]=”name”></h1>

这个 [textContent] 是 DOM 对象的属性,在 html 中我们没有这样的属性。现在,在使用属性绑定时,您应该记住您实际上正在绑定到 DOM 对象的属性,而不是 HTML 元素的属性。在 99% 的情况下,HTML 属性和 DOM 属性具有一对一的映射,但我们在这里有一些例外。所以这里我们处理的是 colspan 属性,如果你想绑定 **td** 元素的这个属性,你需要稍微不同的语法。

@Component({
    selector: 'courses',
    template: `
        <table>
            <tr>
                <td [attr.colspan]="colSpan"></td>
            </tr>
        </table>
    `
})

export class CoursesComponent {
    colSpan = 5;
}

我们只使用,

attr.colspan

通过这种方式,您告诉 Angular 我们正在定位 html 元素的 colspan 属性。

现在我们的浏览器控制台中不再有任何错误。

添加 Bootstrap

是的,如何在 Angular 项目中添加 Bootstrap 是一个非常常见的问题。Bootstrap 实际上是一个 HTML、CSS、Javascript 框架,它使 Web 应用程序具有现代外观。它还有助于设计使其具有响应性。您可以从这里学习 Bootstrap。

现在打开 Visual Studio Code 终端并使用 npm 安装 Bootstrap。

语句:npm install bootstrap --save

我们在这里添加了破折号 --save(没有空格)。

它的作用是,它首先下载 bootstrap 并将其存储在 node_modules 文件夹中,但 save 标志也将 bootstrap 作为依赖项添加到 package.json 中

您可能认为命令不区分大小写,但这里不是这种情况。您必须按照 npmjs 中提到的包名称命名。在搜索栏中搜索,当您在这里找到您的包时,请查看如何在 npm 中编写命令来安装此包。

然后回车,接下来的过程是

在这里您将了解如何使用 npm 安装软件包。但是,如果您输入错误的拼写或任何字母都是大写,那么您将面临错误。

甚至您会了解到如何解决它。

言归正传,现在我们的 bootstrap 3 版本已经安装。

语句:npm install bootstrap@3.3.7 --save

现在打开 package.json。在这里的依赖项列表中,我们有带有版本号的 bootstrap 条目。

"bootstrap": "^3.3.7",

但是这个 (^) 插入符号是什么意思呢?这个版本包含 3 个数字

主要.次要.路径

这个插入符号表示我们可以使用最新的主要版本。所以我们可以使用 3.4、3.5、3.9 版本,但如果有一个更新的主要版本 4.0 或 5.0,我们不会安装它。在这种情况下,我们可以升级到最新的主要版本,即版本 3。

在 Package.Json 中添加条目有什么好处?

当我们将应用程序代码签入到像 git 这样的源代码管理工具时,我们不会签入 node_modules 文件夹。node_modules 文件夹有数十个文件,占用了大量的磁盘空间。因此,通过在 package.json 中列出所有依赖项,任何可以从源代码控制存储库中检出此代码的人。他只需转到终端并在文件的帮助下安装软件包。

现在我们已经下载了 bootstrap,现在我们需要将其样式表导入到我们的 styles.css 中。打开 src > styles.css 文件,这是我们为应用程序添加全局样式的地方。在这里我们将使用 css import 语句并从 node_modules 文件夹中添加 bootstrap css 文件的相对路径。

dist 用于可分发。所以我们需要将这个 bootstrap.css 导入到我们的 styles.css 文件中。

@import "~bootstrap/dist/css/bootstrap.css";

并保存文件。现在打开 courses.component.ts 并编写代码如下

@Component({
    selector: 'courses',
    template: `
        <button class="btn btn-primary">Save</button>
    `
})

然后运行应用程序

Npm 命令:ng serve

它在浏览器中向我显示这个屏幕。

现在让我们在浏览器中做一些正确的事情。在 styles.css 中添加一些填充

@import "~bootstrap/dist/css/bootstrap.css";
body{
    padding: 20px;
}

并保存文件,保存文件后,这里是浏览器视图。

现在好多了。

类绑定

有时我们需要根据某些条件添加额外的类,例如我们希望根据底层组件的状态在按钮元素上应用 active 类,我们为此使用属性绑定的变体。所以让我们从属性绑定开始。

<button class="btn btn-primary" [class.active]>Save</button>

[Class.NameOfTargetClass] 现在将其绑定到组件类中的字段属性。

@Component({
    selector: 'courses',
    template: `
        <button class="btn btn-primary" [class.active]="isActive">Save</button>
    `
})

export class CoursesComponent {
    isActive = true;
}

保存文件并返回浏览器。右键单击保存按钮并检查它,

看这里我们有 3 个类。如果我们更改 CoursesComponent 中 **isActive false** 的值并保存文件

export class CoursesComponent {
    isActive = false;
}

现在我们有 2 个类了。

我们将此语法称为类绑定。

[Class.NameOfTargetClass]=”Property”

现在让我澄清一下,我们将此类绑定与实际的 class 属性分开,因为我们希望始终在按钮上添加 btn btn-primary 类。第三个是动态情况,如果属性为 true,则添加,否则删除,这就是为什么这两个属性是分开的。

样式绑定

我们还有样式绑定,它仍然是属性绑定的变体,但与类绑定非常相似。所以回到我们的例子,如果我们想根据某些条件对这个按钮应用一些内联样式。我们可以这样写,

[style.AnyPropertyOfStyleObjectInDOM]

有关 DOM Style Object 的所有属性的完整内容,请在 Google 上搜索(DOM style object properties),您将看到 w3schools 的第一个链接。

现在我们要根据任何条件设置这个表达式。

@Component({
    selector: 'courses',
    template: `
        <button [style.backgroundColor]="isActive ? 'blue' : 'white'">Save</button>
    `
})

export class CoursesComponent {
    isActive = false;
}

这就是我们所说的样式绑定。

事件绑定

我们已经学习了很多关于属性绑定及其变体,如类绑定、属性绑定、样式绑定,用于在 DOM 中添加内容以显示数据。在 Angular 中,我们还有事件绑定,我们用它来处理 DOM 引发的事件,如按键、鼠标移动、点击等。所以这里我们又有一个简单的按钮。我们将处理这个按钮的点击事件。

所以在这里,现在我们使用括号 () 而不是方括号 [],这里我们添加事件的名称,然后将其绑定到组件中的方法。

import { Component } from '@angular/core';
import { CoursesService } from './courses.service';

@Component({
    selector: 'courses',
    template: `
        <button (click)="onClick()">Save</button>
    `
})

export class CoursesComponent {

    onClick() {
        console.log("Button was Clicked");
    }

}

现在单击按钮,您将在控制台中看到消息。

现在有时我们需要访问在事件处理程序中引发的事件对象,即通过鼠标移动,事件对象将告诉我们 x 和 y 位置。如果您想访问该事件对象,我们需要将其作为参数添加到组件方法中。

@Component({
    selector: 'courses',
    template: `
        <button (click)="onClick($event)">Save</button>
    `
})

export class CoursesComponent {

    onClick($event) {
        console.log("Button was Clicked");
    }

}

这个 $event 对象是 Angular 已知的东西。在这种情况下,我们正在处理 DOM 对象,即按钮。因此,这个 $event 对象将代表我们在 Javascript 和 JQuery 中看到的标准 DOM 事件。

现在让我们记录这个事件。

export class CoursesComponent {

    onClick($event) {
        console.log("Button was Clicked", $event);
    }

}

这里是标准 DOM 事件对象的值,包括许多属性。

所有 DOM 事件,所有 DOM 树,除非沿途的处理程序阻止进一步的冒泡。这只是 DOM 中的标准事件传播机制,它不是 Angular 特有的。

例如,将按钮包装在 div 中,在此 div 中包装 click 事件并绑定它,然后记录消息。

import { Component } from '@angular/core';
import { CoursesService } from './courses.service';

@Component({
    selector: 'courses',
    template: `
        <div (click)="onDivClicked()">
            <button (click)="onClick($event)">Save</button>
        </div>
    `
})

export class CoursesComponent {

    onClick($event) {
        console.log("Button was Clicked", $event);
    }

    onDivClicked(){
        console.log("Salam Muhammad!");
    }

}

现在保存文件并在浏览器中单击按钮。在这里您将看到 2 条消息。

第一条消息是按钮事件,第二条消息是 div。这就是我们所说的 **事件冒泡**。

事件沿着 DOM 树向上冒泡。因此,为了扩展这个例子,我们有另一个 div 或另一个包含这个 div 的元素,我们处理该元素上的点击事件,事件对象将冒泡并击中目标处理程序。

我们如何阻止事件冒泡?

export class CoursesComponent {

    onClick($event) {
        $event.stopPropagation();
        console.log("Button was Clicked", $event);
    }

    onDivClicked(){
        console.log("Salam Muhammad!");
    }

}

同样,这是我们在 Vanilla Javascript 中看到过的标准方法。此方法不会触发第二个处理程序。现在保存并尝试。

事件过滤

在 Angular 中,我们有这个概念叫做事件过滤。所以这里我们有一个输入。假设我们处理事件的键,这里我们也将使用括号。

@Component({
    selector: 'courses',
    template: `
        <input (keyup)="onKeyUp($event)" />
    `
})

export class CoursesComponent {

    onKeyUp($event){
    }

}

这里假设我们只想在用户按下回车键时提交表单或执行某些操作。

export class CoursesComponent {

    onKeyUp($event){
        if ($event.keyCode === 13) {
            console.log("Enter Was Pressed");
        }     
    }

}

这是传统的做法。但在 Angular 中,我们有更好的方法来实现完全相同的功能。所以我们可以在处理事件时应用过滤器。

import { Component } from '@angular/core';
import { CoursesService } from './courses.service';

@Component({
    selector: 'courses',
    template: `
        <input (keyup.enter)="onKeyUp()" />
    `
})

export class CoursesComponent {

    onKeyUp(){
        console.log("Enter Was Pressed");
    }

}

现在这个 onKeyUp 方法只会在我们按下回车键时被调用。现在我们不需要将事件传递给组件函数。现在它更简洁了,让我们尝试一下

模板变量

现在继续最后一个例子,假设我们获得了输入字段中输入的值。我们该怎么做?

有两种方法。

  • 一种是使用事件对象
@Component({
    selector: 'courses',
    template: `
        <input (keyup.enter)="onKeyUp($event)" />
    `
})

export class CoursesComponent {

    onKeyUp($event){
        console.log($event.target.value);
    }

}

$event 是 DOM 中的标准事件对象。它有一个 target 属性,引用了输入字段,然后我们可以获取值。

现在尝试一下,这就是结果

  • 在 Angular 中,我们有另一种方法来解决相同的问题。与其传递 $event 对象,不如在模板中声明一个引用此输入字段的变量。
@Component({
    selector: 'courses',
    template: `
        <input #username (keyup.enter)="onKeyUp(username.value)" />
    `
})

export class CoursesComponent {

    onKeyUp(username){
        console.log(username);
    }

}

所以这种技术使我们的代码更简洁。在很多情况下我们都使用模板变量。现在它与以前一样工作。

双向绑定

在最后一个例子中,我们使用 username 作为参数,但这种方法不太好。在 OOP 中,我们有对象,我们在这里和那里传递对象。因为对象封装了数据和一些行为。所以如果对象拥有它需要的所有数据,我们就不必传递参数。我们实现的最后一个例子是我们所说的**过程式编程**。我们 30 年前在没有 OOP 的时候编写这种代码。

想象一下,如果我们在组件类中有一个字段,那么我们就不必在方法中传递参数。

export class CoursesComponent {
    username;
    onKeyUp(){
        console.log(this.username);
    }
}

现在它更简洁,更容易阅读、理解和维护。另外,如果我们回到 Angular 中组件的定义,请记住组件封装了视图背后的数据、逻辑和 html 标记。这里的 **username** 字段用于封装数据,onKeyUp() 方法是视图背后的逻辑。当然,这里我们也有 html 模板。

我们已经了解了属性绑定,所以我们可以将 DOM 中这个输入对象的 value 属性绑定到这个 username 字段。

@Component({
    selector: 'courses',
    template: `
        <input [value]="username" (keyup.enter)="onKeyUp()" />
    `
})

如果我们用值初始化 username。

export class CoursesComponent {
    username = "Usama Shahid";

    onKeyUp(){
        console.log(this.username);
    }

}

现在当我们加载页面时,username 输入将弹出此值。

但是如果我们在这里更改值并按回车,它不会在控制台上显示更改后的值。

因为属性绑定,在属性绑定中,绑定的方向是从**组件到视图**。所以如果组件字段 username 的值在未来某个时间点发生变化,视图将得到通知,输入字段将自动更新。

现在我们需要稍微不同的绑定,双向绑定(从组件到视图,从视图到组件)

@Component({
    selector: 'courses',
    template: `
        <input [value]="username" (keyup.enter)="username = $event.target.value; onKeyUp()" />
    `
})

事件绑定的值,我们可以像我们在这里写的那样写任何表达式。所以这里我们有 2 个语句。一个用于设置用户名,另一个用于调用函数。现在让我们尝试这段代码

通过这种实现,我们有了双向绑定,这是我们可能在每个应用程序中都需要的东西。我们不应该编写所有这些重复的代码,为此 Angular 有特殊的语法来实现双向绑定。

因此,我们使用双向绑定语法而不是属性绑定语法。在这里我们绑定 ngModel。现在 ngModel 是什么?我们的输入 DOM 对象没有名为 ngModel 的属性,这是 Angular 在此 DOM 对象中添加的内容。我们已经看到了 ngFor 指令,它用于操作 DOM,这里我们使用另一个指令进行双向数据绑定。因此,我们在这里的这种实现以通用方式封装在一个名为 ngModel 的指令中。为此,我们不必每次都重复我们的代码。

import { Component } from '@angular/core';
import { CoursesService } from './courses.service';

@Component({
    selector: 'courses',
    template: `
        <input [value]="username" (keyup.enter)="username = $event.target.value; onKeyUp()" />
        <input [(ngModel)]="username" (keyup.enter)="onKeyUp()" />
    `
})

export class CoursesComponent {
    username = "Usama Shahid";

    onKeyUp(){
        console.log(this.username);
    }

}

您可以看到第二行比以前更简洁、更短。这两个语句只是为了演示。删除第一行并保存文件并运行程序。然后我们在浏览器中得到了错误。

这是一个熟悉的错误。所以基本上我们的输入对象没有名为 ngModel 的属性,这是 Angular 添加的。但是为什么我们会得到这个错误呢?

Angular 框架由几个不同的模块组成,在每个模块中我们都有许多高度相关的构建块。我们有组件、指令、管道,它们彼此高度相关。不是每个应用程序都需要 Angular 中的所有模块,因为当您引入所有这些模块时,会增加代码大小。所以这个 ngModel 指令是在一个名为 **forms** 的模块中定义的。默认情况下,它不会导入到应用程序中。因此,如果您使用 ngModel 或构建任何形式,您需要显式导入此模块。

现在打开 app.module.ts,这里我们有一个 **imports** 数组,其中 BrowserModule 是 Angular 的内置模块,它带来了一些几乎每个浏览器应用程序都需要的功能。这里我们需要在 import 数组中添加 FormsModule

@NgModule({
  declarations: [
    AppComponent,
    CoursesComponent
  ],

  imports: [
    BrowserModule,
    FormsModule
  ],

  providers: [],
  bootstrap: [AppComponent]
})

export class AppModule { }

并在此处导入此语句。

import { FormsModule } from "@angular/forms";

现在查看结果,您将看到我们的双向绑定像以前一样工作。

管道

Angular 中的另一个构建块是管道。我们使用管道来格式化数据,例如我们有一堆内置管道,例如。

  • 大写
  • 小写
  • 十进制
  • 货币
  • 百分比

我们可以创建自己的自定义管道,稍后我们将学习它。但首先让我们看看这些内置管道的实际应用。这里我们在模板 html 中有变量。

@Component({
    selector: 'courses',
    template: `
        {{ courses.title }} <br />
        {{ courses.students }} <br />
        {{ courses.rating | number }} <br />
        {{ courses.price }} <br />
        {{ courses.releaseDate }} <br />
    `
})

export class CoursesComponent {
    courses = {
        title: "Object Oriented Programming",
        rating: 4.9785,
        students: 10000,
        price: 195.123,
        releaseDate: new Date(2018, 6, 16)
    }
}

现在我们如何使用管道来格式化我们的数据。

让我们从标题开始,假设我们想以大写形式显示它。应用管道运算符并写入管道的名称,

我们也可以在这里链式连接多个管道。多个管道语句的工作场景是假设这是语句。

{{ courses.title | uppercase | lowercase }} 

现在小写管道将应用于 (courses.title | uppercase) 这部分的结果。

现在看 10000 位数字,为了使其更具可读性,我们像十进制数字一样每隔 3 位放置一个逗号。所以这里我们将应用 decimal 管道。这个 decimal 管道的关键字是 number,尽管实际的 number 类叫做 decimal 管道。

{{ courses.students | number }} <br />

现在第三个数字是 4.979。现在通过这个数字或小数管道,我们可以控制整数位数以及小数点后的位数。所以语法是,

{{ templatevariable | number:'NumberOfIntegerDigits.NumberOfMinDigits-NumberOfMaxDigits' }}

{{ courses.rating | number:'1.1-2' }} <br />

它向我们显示了四舍五入后的结果。

看,它也四舍五入了我们的值。

现在我们有价格属性,我们需要用货币管道格式化它。

{{ courses.price | currency }} <br />

现在,如果我想在货币管道中应用多个参数,假设我们想将其设置为澳元并显示其符号

{{ courses.price | currency:'AUD':true }} <br />

这是此语句的结果。

如果我们想限制整数的总数和分数后的小数点最小和最大位数。

{{ courses.price | currency:'AUD':true:'3.1-2' }} <br />

这些示例的目的是让您了解货币管道的不同参数。

最后我们得到了发布日期。如果你看上面的图片,你会知道它对人类来说不那么可读。所以这里我们需要应用日期管道

{{ templatevariable | date:'FormatOfDate' }}

如果你想查看所有与 DatePipe 相关的格式。打开 Angular.io 并搜索 DatePipe

在这里您将了解到它定义在 @angular/common 库中。我们的项目默认包含此库。它定义在 CommonModule 中,我们以前没有见过 CommonModule。我们只见过 BrowserModule,但是当你导入 BrowserModule 时,它也带来了 CommonModule。CommonModule 包含所有应用程序(无论是浏览器应用程序还是非浏览器应用程序)所需的大量构件。在此 页面上,您将看到所有可用的格式。

在 AngularJS (1.x) 中,我们有 2 个管道或更准确地说 2 个过滤器用于排序和过滤数据。

  • orderBy
  • 过滤器 (filter)

在 Angular 2 中,由于某些原因,我们没有管道。因为在 Angular 术语中,这些是昂贵的操作。这就是为什么 Angular 团队决定在 Angular 后续版本中放弃这些过滤器。这就是为什么它们现在不存在于当前版本中。

自定义管道

在这里我们将创建一个自定义管道,它可以从大量内容中汇总文本。这是我们的演示代码,

@Component({
    selector: 'courses',
    template: `
        {{ text }}       
    `
})

export class CoursesComponent {
    text = `
    Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.
    `
}

现在让我们创建一个名为 **“summary”** 的管道

{{ text | summary }}       

现在在 app 文件夹中生成一个名为 **‘summary.pipe.ts’** 的新文件。现在在这个文件中,我们需要从 Angular 导入几种类型。

第一个是 Pipe Decorator 函数,第二个是 PipeTransform,它是一个接口,定义了 Angular 中所有管道的形状。

现在,像组件和服务一样,导出 SummaryPipe 类,这里我们需要实现 PipeTransform 接口。

看,这是一个编译错误,因为我们必须在类中实现接口。但是我们不知道接口体。所以来到 Angular.io 并搜索 PipeTransform,这就是 PipeTransform 接口的定义方式。

现在我们需要在我们的类中遵循这个签名。Transform 方法应该有两个参数,一个是 value,类型为 **any**,另一个是 **args**,类型为 **any** 数组。最后,我们需要在这里对类应用 Pipe Decorator 函数,并初始化 name 属性,并用我们在组件模板中应用的关键字填充 name 属性。

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
    name: 'summary'
})

export class SummaryPipe implements PipeTransform {

    transform(value: any, args?: any) {
    }

}

我们的组件模板代码是

{{ text | summary }}       

现在在导出类中,我们将实现一个非常基本的摘要算法,带有一些基本验证。

export class SummaryPipe implements PipeTransform {

    transform(value: any, args?: any) {
        if (!value)
            return null;
    }

}

我们已经知道任何类型都可以是任何东西,如果你想在 VS Code 中获得智能感知,那么将类型更改为字符串。

这是我们的代码,

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
    name: 'summary'
})

export class SummaryPipe implements PipeTransform {

    transform(value: string, args?: any) {
        if (!value)
            return null;

        return value.substr(0, 50) + '...';
    }

}

现在保存文件并运行应用程序。但是在这里您将看到一个空白屏幕,控制台中显示“Pipe 'summary' not define”错误。由于我们正在创建一个自定义管道,因此显然我们也需要在 app 模块中注册它。所以打开 app.module.ts 并在 app 模块声明中注册 SummaryPipe。声明块是关于注册组件和管道的。自动导入插件会自动添加 SummaryPipe 的引用。

  declarations: [
    AppComponent,
    CoursesComponent,
    SummaryPipe
  ],

这是代码,如果您尚未安装自动导入或 VS Code。那么您可以手动添加引用。

import { SummaryPipe } from './summary.pipe';

现在保存 app module 文件,然后在 SummaryPipe 文件中再次添加一个空格键,以便为 SummaryPipe 文件运行 webpack,以生成正确的编译应用程序版本。这是基本概念,库在前,然后是我们编写的代码。同样,如果您先编译了代码但没有包含库,然后在添加库之后。不要指望已经生成了正确的编译版本,除非您的库在前,然后是我们的代码。

现在让我们更进一步,回到组件中,我希望能够在这里提供参数。

@Component({
    selector: 'courses',
    template: `
        {{ text | summary }}       
    `
})

让我们将字符数与摘要一起应用。

        {{ text | summary:10 }}       

为了实现这一点,让我们转到 SummaryPipe。为了更容易理解,更改参数的名称和类型。

export class SummaryPipe implements PipeTransform {

    transform(value: string, limit?: number) {
        if (!value)
            return null;

        let actualLimit = limit ? limit : 50;
        return value.substr(0, actualLimit) + '...';
    }
}

现在运行应用程序。

它正在成功运行。现在您可能想在自定义管道中添加更多参数,那么不用担心,只需在 transform 方法中添加参数,稍后在主体中使用它们即可。

transform(value: string, limit?: number, anotherValue?: string, name?: string)
{

}

我们学到了什么

现在让我们根据我们在本文中学到的内容构建一些东西。

任务 1

您可能曾向 Google 查询您的问题,大多数情况下我们会得到 stackoverflow 的结果。在 stackoverflow 的每个问题中,都有一个将任何问题设为精选的功能。

让我们在 Angular 中构建这个星标功能。我们该怎么做?

正如我们已经讨论过的,我们使用组件是为了重用。我们需要此功能时,只需一次又一次地调用此特定组件。让我们来看看解决方案。

首先,让我们使用 Angular CLI 创建组件。

PS C:\Users\Ami Jan\Dummy> cd ./MyDummyProject/
PS C:\Users\Ami Jan\Dummy\MyDummyProject> ng g c star

现在打开 star.component.ts。当我们要更改任何问题的状态时,**bit/boolean** 数据类型会浮现在我们的脑海中,我们只是将某些东西设为 true false 或 0 1。所以让我们在 star.component.ts 中创建一个布尔变量。

export class StarComponent implements OnInit {

  isFavorite: boolean;

  constructor() { }

  ngOnInit() {
  }

}

当我们通过 Angular CLI 构建组件时,样板代码包含组件选择器为 **'app-NameofComponent'**。实际上它有助于减少我们在 Angular 中使用第三方库时的冲突。但是我们只是为了练习。所以如果您在组件选择器中删除 **app-** 前缀,则没有问题。

@Component({
  selector: 'star',
  templateUrl: './star.component.html',
  styleUrls: ['./star.component.css']
})

现在保存组件文件并打开其 html 文件。如果您打开 bootstrap 3 网站,您将在这里的组件部分看到字形图标。虽然它们在 bootstrap 4 中已过时,但如果您使用的是 bootstrap 3,您仍然可以轻松使用这些字形图标。在这里我们有 **星形字形图标**,所以让我们在组件 html 中使用这些字形图标类。

<span class="glyphicon"
      [class.glyphicon-star]="isFavorite"
      [class.glyphicon-star-empty]="!isFavorite"
></span>

看这里我们使用属性绑定语法来绑定 star 组件的 **isFavorite** 变量。我们已经知道属性绑定只从组件到视图单向工作。所以这里我们也使用事件绑定语法来改变组件变量的值,当 **isFavorite** 变量更新其值时,我们可以成功执行我们的两个类绑定属性,如果 **isFavorite** 为 false,那么 **glyphicon-star-empty** 类将附加到 html 元素,如果为 true,那么显然 **glyphicon-star** 就会出现。

<span class="glyphicon"
      [class.glyphicon-star]="isFavorite"
      [class.glyphicon-star-empty]="!isFavorite"
      (click)="onClick()"
></span>

现在我们的 star.component.ts 代码是

export class StarComponent implements OnInit {

  isFavorite: boolean;

  constructor() { }

  ngOnInit() {
  }

  onClick() {
    this.isFavorite = !this.isFavorite;
  }

}

在这里,我们的 onClick() 函数在点击事件上更新值。如果 isFavorite 为 true,则变为 false;如果为 false,则变为 true。现在是时候使用我们的星形组件了。所以让我们打开 app.component.html 并在那里放置星形组件选择器。

<star></star>

现在保存所有文件并观察结果。是的,它如我们所愿成功运行。

2

现在让我们使用自定义管道制作一些新的东西。让我们制作一个输入文本框,我们在其中输入一些内容,它会在文本框下方屏幕上原样打印。但是在完整的句子中,我们将介词打印为小写,其余文本每个单词的首字母大写。

打开 app.component.html 并粘贴代码,

<input type="text" [(ngModel)]="title" >

<br/>

{{ title }}

这就是我们在屏幕上回显代码的方式。你可能在想我为什么不在这里创建一个额外的组件。因为这里我们只是显示在文本框中输入的数据,没有任何额外的操作。还要确保你已经在 app.module.ts 中导入了 **FormsModule**,因为 ngModel 是在 FormsModule 中定义的。现在打开 **app.component.ts**

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

export class AppComponent {
  title: string;
}

这里我们定义了类型为字符串的 title 变量。现在我们想创建一个自定义管道,在其中编写代码来格式化数据。所以,

PS C:\Users\Ami Jan\Dummy\MyDummyProject> ng g p title-case

这是我们的管道代码。

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'titleCase'
})

export class TitleCasePipe implements PipeTransform {

  transform(value: string): any {
    if (!value)
      return null;

    let prepositions = [
      'of',
      'the'
    ];

    let words = value.split(' ');

    for (let i = 0; i < words.length; i++) {
      if (i > 0 && prepositions.includes(words[i].toLowerCase()))
        words[i] = words[i].toLowerCase();
      else
        words[i] = words[i].substr(0, 1).toUpperCase() + words[i].substr(1).toLowerCase();
    }

    return words.join(' ');
  }
}

英语有 40 多个介词,但在这里我们只是为了学习和测试目的处理这些事情。让我逐行详细说明代码。

    let prepositions = [
      'of',
      'the'
    ];

所以这是我们检查完整句子中的介词列表。

let words = value.split(' ');

split 函数实际上返回一个字符数组。在 split 函数中,我们提供了您在何处打断句子的字符。由于一个句子中有很多空格,所以句子会分解成单词,所有单词都以数组的形式进入 **words 变量**。

for (let i = 0; i < words.length; i++) {
      if (i > 0 && prepositions.includes(words[i].toLowerCase()))
        words[i] = words[i].toLowerCase();
      else
        words[i] = words[i].substr(0, 1).toUpperCase() + words[i].substr(1).toLowerCase();
}

在这里,我们遍历数组元素的长度,并检查介词是否包含我们在循环计数器 **(words[i])** 中拥有的单词,然后我们将这些单词转换为小写并将其重新分配回数组元素的位置。如果单词不属于介词数组,我们将其首字母大写,其余字母小写。还有一件事是,如果它是句子的开头,我们不应用这些单词的更改,所以我们使用 **i > 0** 来排除这个 s

现在是时候在元素上应用管道了。

{{ title | titleCase }}

现在保存所有文件并测试程序。是的,它现在正在成功运行。它打印出句子中除介词外的所有单词的首字母大写。

结论

在这里我们学到了很多东西,我们看到了数据绑定不同的变体方法,甚至是如何绑定类、属性、样式和事件。我们学习了如何向应用程序添加包以及如何过滤应用程序中的事件。我们看到了不同的编程方法,如过程式和面向对象。我们看到了如何启用双向绑定以及如何使用管道格式化数据。如果某些情况下我们需要开发自定义管道。那么我们也开发了一个自定义管道。DOM 与 HTML 最有趣的概念。老实说,当我写它时,我个人非常喜欢这个概念。事件冒泡也非常有用,您应该了解它。这就是 Javascript 系列语言在底层的工作方式。现在我们对在屏幕上显示数据以及事物如何工作有了很多经验。

© . All rights reserved.