学习 Angular 教程 - 第四部分





5.00/5 (3投票s)
进行 HTTP 调用并使用 Input 和 Outputs 创建自定义 Angular 组件
目录
其余文章的链接
- 在第一部分中,我们探讨了 Node、TypeScript、模块加载器/打包器和 VS Code。
- 在第二篇文章中,我们创建了一个简单的 Angular 应用程序,其中包含一个屏幕,并回顾了组件和模块等重要概念
- 在第三篇文章中,我们研究了如何实现 SPA 和验证。
- 在第四篇文章中,我们理解了如何进行 HTTP 调用以及如何使用 Input 和 Output 创建自定义 Angular 组件。
- 在第五部分中,我们涵盖了两个实验——一个是如何在 Angular 中使用 Jquery,另一个是延迟路由。
- 在第六部分中,我们再次涵盖了两个实验——管道和使用提供者的依赖注入。
Lab 9:进行 HTTP 调用
服务器端交互的重要性
如果没有服务器端交互,HTML 用户界面就毫无意义。通常在 Web 应用程序中,我们希望将最终用户输入的数据发送到服务器。在服务器端,我们会有某种服务,可以使用 Java、C# 等技术来创建。服务器端技术可以在数据库中保存、编辑或删除这些数据。
简单来说,我们需要了解如何从 Angular 代码向服务器端技术发出 HTTP 调用。
是的,这是一篇纯 Angular 文章。
我打算让这篇文章保持纯 Angular 的风格。所以像 ASP.NET MVC、Java 服务、PHP 这样的服务器端技术教学超出了范围。 因此,服务器端服务将使用“Angular inmemory Webapi”被“伪造”(Mocked)。请注意,这不是您将用于生产环境的服务。它仅用于测试和开发目的。 如果您想学习 ASP.NET MVC,可以从这个视频开始。 | |
步骤 1:创建假的服务器端服务
尽管我们已经决定不使用 ASP.NET、Java 等创建专业的服务器端服务,但我们仍然需要一个。所以我们将使用一个名为“Angular in-memory web api”的假服务。
当我们进行 npm 安装时,“Angular inmemory web api”已经被安装。您可以在“package.json”文件中查看“Angular inmemory web api”的条目。
当我们进行 npm 安装时,“Angular inmemory web api”已经被安装。您可以在“package.json”文件中查看“angular inmemory web api”的条目。
所以让我们创建一个名为“Api”的文件夹,并在其中添加“CustomerApi.ts”文件,在该文件中,我们将编写代码来创建一个假的客户服务。 在这个假服务上,我们将进行 HTTP 调用。 | ![]() |
下面是一个使用“angular-in-memory”开源项目创建的简单服务。在这个服务中,我们加载了一个简单的“customers
”集合,其中包含一些示例客户记录。
import { InMemoryDbService } from 'angular-in-memory-web-api'
import {Customer} from "../Model/Customer"
export class CustomerApiService implements InMemoryDbService {
createDb() {
let customers =[
{ CustomerCode: '1001', CustomerName: 'Shiv' , CustomerAmount :100.23 },
{ CustomerCode: '1002', CustomerName: 'Shiv1' , CustomerAmount :1.23 },
{ CustomerCode: '1003', CustomerName: 'Shiv2' , CustomerAmount :10.23 },
{ CustomerCode: '1004', CustomerName: 'Shiv3' , CustomerAmount :700.23 }
]
return {customers};
}
}
所以现在当 Angular 进行 HTTP 调用时,它将命中这个内存中的 API。 因此,当您发出 HTTP 换句话说,我们不需要使用 ASP.NET 或 Java 服务创建专业的服务器端服务。 | |
步骤 2:将 HTTP 和 WebAPI 模块导入到主模块
下一步是从“angular/http
”导入“HttpModule
”以及内存中的 API 到主模块。请记住,模块是组件的集合。所以“MainModule
”包含“CustomerComponent
”、“SupplierComponent
”、“MasterpageComponent
”和“WelcomeComponent
”。
import { HttpModule} from '@angular/http';
import {CustomerApiService} from "../Api/CustomerApi"
同样在 imports 的“NgModule
”属性中,我们需要指定“HttpModule
”和“InMemoryWebApiModule
”。
@NgModule({
imports: [….
HttpModule,
InMemoryWebApiModule.forRoot(CustomerApiService),
…..],
declarations: […],
bootstrap: [….]
})
export class MainModuleLibrary { }
Shiv:谈谈 Httpmodule
和 Angular Webapi 的顺序。
HTTP 调用放在哪里?
下一个问题是,将 HTTP 调用放在什么地方是正确的?
所以,如果您看到 Angular 的正常架构,它是这样的:
| ![]() |
步骤 3:在 Customer 组件中导入 HTTP
所以,让我们继续在 Customer
组件中导入 Angular HTTP。
import { Http, Response, Headers, RequestOptions } from '@angular/http';
我们不需要使用 new
关键字创建 HTTP 对象,它将通过构造函数进行依赖注入。所以在 constructor
组件中,我们已经定义了 HTTP 的对象注入。
constructor(public http:Http){
}
步骤 4:创建头部和请求信息
当我们将 HTTP 请求从客户端发送到服务器或接收响应时,头部信息会随请求和响应一起传递。在头部信息中,我们有内容类型、状态、用户代理等。 | |
所以要创建一个请求,我们需要先创建一个头部,并使用该头部创建一个请求。我们需要提供的头部信息之一是我们发送到服务器的内容类型 - 是 XML、JSON 等。
下面是如何使用基本头部信息创建一个简单请求。
let headers = new Headers({'Content-Type': 'application/json'});
let options = new RequestOptions({ headers: headers });
步骤 5:进行 HTTP 调用和 Observable
Angular HTTP 使用一种称为 observable
的东西。所以 Angular 是一个观察者,它订阅 observable
就像 HTTP 响应一样。换句话说,它正在监听来自服务器的数据。
所以下面的代码表示我们将向“api/customers”URL 发出 GET
调用,当数据到来时,我们将成功的数据发送到“Success
”函数,并在发生错误时,在“Error
”函数中捕获它。
var observable = this.http.get("api/customers", options);
observable.subscribe(res => this.Success(res),
res => this.Error(res));
下面是 Error
和 Success
函数的代码。在“Success
”函数中,我们将响应转换为 JSON 并将其赋值给组件中的“Customers
”集合。如果出现错误,我们将在浏览器控制台中显示它。
Error(err) {
console.debug(err.json());
}
Success(res) {
this.Customers = res.json().data;
}
步骤 6:创建一个简单的 POST 和 GET 调用
结合我们从步骤 4 和步骤 5 中收集到的所有知识,让我们写两个函数,一个用于显示数据,另一个用于发布数据。
下面是一个简单的“Display
”函数,它发出一个 HTTP GET
调用。
Display(){
let headers = new Headers({
'Content-Type': 'application/json'
});
let options = new RequestOptions({ headers: headers });
var observable = this.http.get("api/customers", options);
observable.subscribe(res => this.Success(res),
res => this.Error(res));
}
一旦 customer UI 加载完毕,customercomponent
对象就会被创建。所以我们在 constructor
中调用了“Display
”函数来加载 customers 集合。
export class CustomerComponent {
// other code removed for clarity
constructor(public http:Http){
this.Display();
}
// other codes removed for clarity
}
下面是一个简单的“Add
”函数,它向服务器发出 POST
调用。在下面的 http POST
调用代码中,您可以看到 customer
对象作为第三个参数发送。在“Add
”调用之后,我们调用了“Display
”,以便我们可以看到新添加到服务器的数据。
Add(){
let headers = new Headers({
'Content-Type': 'application/json'
});
var cust:any = {};
cust.CustomerCode = this.CurrentCustomer.CustomerCode;
cust.CustomerName = this.CurrentCustomer.CustomerName;
cust.CustomerAmount = this.CurrentCustomer.CustomerAmount;
let options = new RequestOptions({ headers: headers });
var observable = this.http.post("api/customers",cust, options);
observable.subscribe(res => this.Success1(res),
res => this.Error(res));
this.Display();
}
在上面的“Add
”函数中,您可以看到下面的代码创建了一个新的轻量级 customer
对象。那么为什么我们需要创建这个新的对象呢?
var cust:any = {};
cust.CustomerCode = this.CurrentCustomer.CustomerCode;
cust.CustomerName = this.CurrentCustomer.CustomerName;
cust.CustomerAmount = this.CurrentCustomer.CustomerAmount;
当前的 换句话说,我们需要创建一个轻量级的 DTO(Data Transfer Object),它只包含这些属性。 | ![]() |
步骤 7:将组件连接到用户界面
现在我们的组件已经完成,我们需要使用 Angular 的“click
”事件将“Add
”函数绑定到按钮上。您可以看到 (click) 在圆括号内,换句话说,我们正在将某些东西(单击事件)从 UI 发送到对象。
<input type="button"
value="Click" (click)="Add()" [disabled]="!(CurrentCustomer.formGroup.valid)"/>
另外,我们需要创建一个表,在该表中我们将使用“ngFor
”循环并在 HTML UI 上显示 customers 集合。在下面的代码中,我们创建了一个临时对象“cust
”,它遍历“Customers
”集合,并在 tag 中,我们使用表达式({{cust.CustomerName}}
)来显示数据。
<table>
<tr>
<td>Name</td>
<td>code</td>
<td>amount</td>
</tr>
<tr *ngFor="let cust of Customers">
<td>{{cust.CustomerName}}</td>
<td>{{cust.CustomerCode}}</td>
<td>{{cust.CustomerAmount}}</td>
</tr>
</table>
继续运行应用程序。如果您添加“customer
”对象,您应该会看到 HTTP 调用正在发生,并且列表正在 HTML 表中加载。
Lab 10:输入、输出和发射器
理论
可重用性是开发中最重要的方面之一。由于 Angular 是一种 UI 技术,我们希望创建 UI 控件并在不同的 UI 中重用它们。 例如,在 | ![]() |
如果我们想创建通用的可重用网格控件,您需要进行通用思考,您需要从输入和输出方面进行思考。 所以,如果您将此网格控件与 | ![]() |
为了实现这种通用思维过程,Angular 提供了三样东西:
| ![]() |
规划组件的外观
首先,让我们规划一下我们的网格组件的外观。Grid 组件将在主 HTML 中使用“<grid-ui></grid-ui>
” HTML 元素调用。Grid 组件将有三个属性:
grid-columns
:这将是一个输入,我们将在其中指定网格的列名。grid-data
:这又将是一个输入,我们将提供要在网格上显示的数据。grid-selected
:这将是一个输出,通过发射器事件将选定的对象发送到包含的 UI。
<grid-ui [grid-columns]=”In this we will give column names"
[grid-data]="In this we will give data for grid"
(grid-selected)="The selected object will be sent in event">
</grid-ui>
步骤 1:导入 Input、Output 和 Event Emitter
所以,首先,让我们为网格组件代码添加一个单独的文件,并将其命名为“GridComponent.ts”。
在此文件中,我们将编写 Grid
组件所需的所有代码。
第一步是添加必要的组件,这些组件将引入 Input
和 Output
功能。为此,我们需要从“angular/core”导入 component、Input
、Output
和 event
emitter 组件。
import {Component,
Input,
Output,
EventEmitter} from "@angular/core"
步骤 2:创建可重用的 GridComponent 类
由于这是一个 component
,我们需要使用“@Component
”装饰器来装饰类。这个 component
的 UI 将在“Grid.html”中编写。您也可以在下面的代码中看到我们定义了选择器为“grid-ui
”,您能猜到为什么吗?
如果您还记得我们在规划阶段,我们说过网格可以通过“<grid-ui>
”标签调用。
@Component({
selector: "grid-ui",
templateUrl : "../UI/Grid.html"
})
export class GridComponent {
}
步骤 3:定义输入和输出
由于这是一个网格组件,我们需要网格的数据和列名。所以我们创建了两个数组集合:一个是“gridColumns
”(包含列名),另一个是“gridData
”(用于表格数据)。
export class GridComponent {
// This will have columns
gridColumns: Array<Object> = new Array<Object>();
// This will have data
gridData: Array<Object> = new Array<Object>();
}
有两个方法“setGridColumns
”和“setGridDataSet
”,它们将帮助我们为上面定义的两个变量设置列名和表格数据。
这些方法将使用“@Input
”装饰器进行装饰,在其中,我们将放置在调用此组件时调用这些输入的名称。
// The top decorator and import code is removed
// for clarity purpose
export class GridComponent {
// code removed for clarity
@Input("grid-columns")
set setgridColumns(_gridColumns: Array<Object>) {
this.gridColumns = _gridColumns;
}
@Input("grid-data")
set setgridDataSet(_gridData: Array<Object>) {
this.gridData = _gridData;
}
}
输入装饰器中定义的名称将如下所示,在主 UI 中调用该组件时使用。
<grid-ui [grid-columns]=”In this we will give column names"
[grid-data]="In this we will give data for grid" >
</grid-ui>
步骤 4:定义 Event Emitters
正如在本实验的理论部分所讨论的,我们将有输入和输出。输出再次使用“@Output
”装饰器定义,数据通过事件发射器对象发送。
要定义输出,我们需要使用“@Output
”装饰器,如下面的代码所示。此装饰器定义在“EventEmitter
”对象类型之上。您可以看到类型是“Object
”,而不是“Customer
”或“Supplier
”,因为我们希望它是通用类型的。这样我们就可以将此输出与任何组件类型关联。
@Output("grid-selected")
selected: EventEmitter<Object> = new EventEmitter<Object>();
现在,当任何最终用户从网格中选择一个对象时,我们需要使用“EventEmitter
”对象通过调用“emit
”方法来引发事件,如下所示。
// Other codes have been removed for clarity purpose.
export class GridComponent {
@Output("grid-selected")
selected: EventEmitter<Object> = new EventEmitter<Object>();
Select(_selected: Object) {
this.selected.emit(_selected);
}
}
下面是到目前为止我们讨论过的“GridComponent.ts”的完整代码。
import {Component,
Input,
Output,
EventEmitter} from "@angular/core"
@Component({
selector: "grid-ui",
templateUrl : "../UI/Grid.html"
})
export class GridComponent {
gridColumns: Array<Object> = new Array<Object>();
// inputs
gridData: Array<Object> = new Array<Object>();
@Output("grid-selected")
selected: EventEmitter<Object> = new EventEmitter<Object>();
@Input("grid-columns")
set setgridColumns(_gridColumns: Array<Object>) {
this.gridColumns = _gridColumns;
}
@Input("grid-data")
set setgridDataSet(_gridData: Array<Object>) {
this.gridData = _gridData;
}
Select(_selected: Object) {
this.selected.emit(_selected);
}
}
步骤 5:为可重用组件创建 UI
另外,我们需要为“GridComponent.ts”创建 UI。请记住,如果我们有一个 Angular 组件,我们就需要一个 HTML UI!
所以,在 UI 文件夹中,我们将添加“Grid.html”,其中我们将编写表格显示的代码。
在“GridComponent.ts”(请参阅本实验的步骤 4)中,我们定义了输入变量“gridColumns
”,其中我们将提供网格的列。所以为此,我们使用“*ngFor
”创建了一个循环,它将动态创建“<td>
”列。
<table>
<tr>
<td *ngFor="let col of gridColumns">
{{col.colName}}
</td>
</tr>
</table>
为了在网格中显示数据,我们需要遍历“gridData
”变量。
{{colObj[col.colName]}}
<a>Select</a>
因此,“Grid.html”的完整代码如下所示。
<table>
<tr>
<td *ngFor="let col of gridColumns">
{{col.colName}}
</td>
</tr>
<tr *ngFor="let colObj of gridData">
<td *ngFor="let col of gridColumns">
{{colObj[col.colName]}}
</td>
<td><a [routerLink]="['Customer/Add']"
(click)="Select(colObj)">Select</a></td>
</tr>
</table>
步骤 6:在 Customer UI 中使用该组件
现在我们的可重用组件及其 UI 已经完成,让我们在“Customer.html” UI 中调用该组件。
下面是完整的正确代码,其中“grid-columns
”定义了列名,在“grid-data
”中,我们设置了“Customers
”集合。这个“Customers
”集合对象是从“CustomerComponent
”类暴露出来的。这个“Customers
”集合是我们之前在“HTTP”调用实验中创建的集合。该变量包含“Customer
”数据的集合。
<grid-ui
[grid-columns]="[{'colName':'CustomerCode'},
{'colName':'CustomerName'},{'colName':'CustomerAmount'}]"
[grid-data]="Customers"
(grid-selected)="Select($event)"></grid-ui>
此外,我们需要确保删除旧的“<table>
”代码,并用上面的“<grid-ui>
”输入/输出标签替换它。
步骤 7:在主 Customer 组件中创建事件
如果您还记得在我们的“ | ![]() |
export class GridComponent {
Select(_selected: Object) {
this.selected.emit(_selected);
}
}
因此,为了在主组件中捕获该事件,我们需要在“CustomerComponent”文件中创建一个方法。所以,在 customer 组件的 typescript 文件中,我们将创建一个“Select
”函数,该函数将从 GridComponent
接收选定的 customer
,并将该选定的对象设置为“CurrentCustomer
”对象。
export class CustomerComponent {
Select(_selected:Customer) {
this.CurrentCustomer.CustomerAmount =_selected.CustomerAmount;
this.CurrentCustomer.CustomerCode = _selected.CustomerCode;
this.CurrentCustomer.CustomerName = _selected.CustomerName;
}
}
步骤 8:在主模块中定义组件
另外,我们需要确保“GridComponent
”已加载到主模块中。所以在主模块中,导入“GridComponent
”并将其包含在“NgModule
”装饰器的声明中,如以下代码所示。
import { GridComponent } from '../Component/GridComponent';
@NgModule({
imports: [RouterModule.forRoot(ApplicationRoutes),
InMemoryWebApiModule.forRoot(CustomerApiService),
BrowserModule,ReactiveFormsModule,
FormsModule,HttpModule],
declarations: [CustomerComponent,
MasterPageComponent,
SupplierComponent,
WelcomeComponent,
GridComponent],
bootstrap: [MasterPageComponent]
})
export class MainModuleLibrary { }
就是这样!按 Control + B,运行服务器,看看您的可重用网格是否正常工作。
下一篇文章有什么内容?
在第 5 部分,我们将研究如何进行动态路由以及如何在 Angular 中使用 Jquery。
如需进一步阅读,请观看下面的面试准备视频和逐步视频系列。
- Angular 面试问答
- ASP.NET MVC 面试题及答案
- C# 面试问答
- 分步学习 Azure
- SQL Server 分步教程
- RxJS 面试问答
- 1 小时学习 Angular(Angular 9)
- 8 小时分步学习 Angular
历史
- 2017 年 9 月 22 日:初始版本