Angular入门 从零开始,一个月内能够制作一个服务的能力 之四:模型、循环和分支
上一次,我们创建了多个页面并尝试了页面跳转,从而学习了路由的基础。
本文将讨论
-
- Angularでのデータ管理の基礎
モデルクラスの作成
Angularのディレクティブ基礎
HTMLでの分岐
HTMLでのループ
我会继续做下去。
这篇文章的源代码
这是一个关于Angular Guides的Github页面,地址是https://github.com/seteen/AngularGuides/tree/入門その04。
Angular数据管理的基础
上次,我创建了product-list.component和product-detail.component。
在之前,这些名称并没有特别的意义,但是从这次开始,我想要给它们赋予含义。
从现在开始,我们将逐步创建各个页面的内容,但在创建这些页面时,重要的是让各个组成部分共同理解商品的定义及其特点。
为了在Angular项目中建立共识,需要创建一个模型文件。
由于该文件将在项目中共同使用,所以需要创建一个通用的文件。
src/app/shared/models
保存。
提示:文件的布局
作为Angular,没有强制要求将每个文件放在哪里。
我认为最好将文件放在参与项目的工程师内部认为合适的地方。
请参考Angular官方指南。
https://angular.io/guide/styleguide#overall-structural-guidelines
创建product.ts模型
在商品中,会包含可以唯一标识的ID,名称,价格和描述元素。
export class Product {
id: number;
name: string;
price: number;
description: string;
constructor(id, name, price, description) {
this.id = id;
this.name = name;
this.price = price;
this.description = description;
}
}
通过在共享文件中定义产品模型,我们可以在项目中形成对产品的共同认识。由于这是TypeScript,只了解JavaScript的人可能会感到不适应。下次我们将解释TypeScript的写法等等。现在,只需要大致了解这个即可。
从组件中使用模型
既然我们已经创建了Product模型,那么让我们在product-detail.component.ts文件中实际显示一下。
import { Component, OnInit } from '@angular/core';
import { Product } from '../../shared/models/product';
@Component({
selector: 'app-product-detail',
templateUrl: './product-detail.component.html',
styleUrls: ['./product-detail.component.scss'],
})
export class ProductDetailComponent implements OnInit {
product: Product;
constructor() { }
ngOnInit() {
this.product = new Product(1, 'Angular入門書「天地創造の章」', 3800, '神は云った。「Angularあれ」。するとAngularが出来た。');
}
}
<div class="container">
<div class="title">商品詳細</div>
<div class="product-detail-container">
<div class="param-line">
<div class="label">ID</div>
<div class="value">{{ product.id }}</div>
</div>
<div class="param-line">
<div class="label">名前</div>
<div class="value">{{ product.name }}</div>
</div>
<div class="param-line">
<div class="label">価格</div>
<div class="value">{{ product.price }}</div>
</div>
<div class="param-line">
<div class="label">説明</div>
<div class="value">{{ product.description }}</div>
</div>
</div>
<div class="footer">
<div class="button black" routerLink="/products">商品一覧へ</div>
</div>
</div>
.container {
margin: auto;
padding: 32px 0;
width: 800px;
.title {
padding: 8px 0;
text-align: center;
width: 100%;
font-weight: 600;
font-size: 18px;
}
.product-detail-container {
padding-left: 48px;
border: 1px solid #D9DBDE;
border-radius: 4px;
background-color: #FFFFFF;
.param-line {
display: flex;
padding: 16px 8px;
&:nth-child(n+2) {
border-top: 1px solid #D9DBDE;
}
.label {
width: 100px;
}
}
}
.footer {
display: flex;
justify-content: ce;
margin-top: 20px;
}
}
修改上述内容后,使用 ng serve 启动,并访问 http://localhost:4200/products/test。
应该在屏幕上显示产品的信息。
解释
尽管HTML和CSS变得稍微复杂了一些,但基本上,我们做的就是与之前在入门第2部分中修改app.component.ts时相同的事情。
产品详情组件.ts
在使用Angular CLI创建组件时,生成的文件中从一开始就包含了ngOnInit()方法。由于还没有对此进行解释,现在来进行解释。
Angular的组件提供了一些生命周期事件钩子。换句话说,组件在创建后到销毁之前有几个预定义的事件,并且这些事件有特定的TypeScript方法名。
例如,可以利用适当的方法来调整显示,因为每个方法都有处理需要在组件创建时执行的操作,以及需要在组件绘制时执行的操作。
其中最常使用的是 ngOnInit()。ng 是从 Angular 中的 ng 中提取的,因此 ng + OnInit 即表示在初始化后调用的方法。
或许你会想知道constructor与ngOnInit有何不同,但就目前而言,我们不必在意这一点,因为把初始处理写在ngOnInit()中可以确保安全且不出现意外的行为,所以让我们将初始化处理写在这里吧。(初学阶段先试试看是很重要的,做着做着就会基本明白了)
提示:生命周期钩子
您还可以查看Angular官方网站,了解其他类型的钩子。
https://angular.io/guide/lifecycle-hooks
产品详情.component.html
使用{{}}来显示Product的各个元素。在{{}}中可用的是与Typescript组件相关的公有实例变量和方法的类。如果您想在HTML中使用某个值,请将其设置为实例变量或方法(如果有WebStorm,则会有自动补全,因此可以立即了解可用的信息)。
产品详情组件样式
这段代码是为了让外观看起来最好而编写的最简洁代码。
由于组件之间使用了不同的CSS,所以可以自由定义并编写类名。
因为这是关于Angular入门的问题,所以不会深入讨论内容,但我非常喜欢 display: flex。
实际的提交
Angular的指令基础(使用 *ngIf 在HTML中分支)
现在我们转到商品列表页面(产品列表组件)。
在创建商品清单之前,我们将创建一个显示来获取商品的过程。(显示API获取商品信息的过程)
<div class="container">
<div class="title">商品一覧</div>
<div class="product-list-container">
<div class="waiting-for-products">
商品を取得しています...
</div>
</div>
</div>
.container {
margin: auto;
padding: 32px 0;
width: 800px;
.title {
padding: 8px 0;
text-align: center;
width: 100%;
font-weight: 600;
font-size: 18px;
}
}
只改变了HTML和SCSS。
让我们在当前状态下查看屏幕(ng serve -> 访问http://localhost:4200/products)。
当获取到数据时,使其显示为不同的形式。
接下来,假设API访问已经结束并成功获得商品信息(现在还没有进行API访问),让我们尝试在获取商品信息之前和之后更改显示。
import { Component, OnInit } from '@angular/core';
import { Product } from '../../shared/models/product';
@Component({
selector: 'app-product-list',
templateUrl: './product-list.component.html',
styleUrls: ['./product-list.component.scss']
})
export class ProductListComponent implements OnInit {
products: Product[] = null;
constructor() { }
ngOnInit() {
setTimeout(() => {
this.products = [
new Product(1, 'Angular入門書「天地創造の章」', 3800, '神は云った。「Angularあれ」。するとAngularが出来た。'),
new Product(2, 'Angularを覚えたら、年収も上がって、女の子にももてて、人生が変わりました!', 410, '年収300万のSEが、Angularと出会う。それは、小さな会社の社畜が始めた、最初の抵抗だった。'),
new Product(3, '異世界転生から始めるAngular生活(1)', 680,
'スパゲッティの沼でデスマーチ真っ最中の田中。過酷な日々からの現実逃避か彼は、異世界に放り出され、そこでAngularの入門書を拾う。現実逃避でさえ、プログラミングをするしかない彼に待ち受けるのは!?'),
];
}, 3000);
}
}
<div class="container">
<div class="title">商品一覧</div>
<div class="product-list-container">
<div class="waiting-for-products" *ngIf="products === null; else productList">
商品を取得しています...
</div>
<ng-template #productList>
<div class="product-list-table">
<div class="product-line header">
<div class="product-id">ID</div>
<div class="product-name">名前</div>
<div class="product-price">価格</div>
</div>
<div class="product-line">
<div class="product-id">{{ products[0].id }} </div>
<div class="product-name">{{ products[0].name }}</div>
<div class="product-price">{{ products[0].price }}</div>
</div>
<div class="product-line">
<div class="product-id">{{ products[1].id }} </div>
<div class="product-name">{{ products[1].name }}</div>
<div class="product-price">{{ products[1].price }}</div>
</div>
<div class="product-line">
<div class="product-id">{{ products[2].id }} </div>
<div class="product-name">{{ products[2].name }}</div>
<div class="product-price">{{ products[2].price }}</div>
</div>
</div>
</ng-template>
</div>
</div>
.container {
margin: auto;
padding: 32px 0;
width: 800px;
.title {
padding: 8px 0;
text-align: center;
width: 100%;
font-weight: 600;
font-size: 18px;
}
.product-list-container {
.product-list-table {
border: 1px solid #D9DBDE;
border-radius: 4px;
overflow: hidden;
.product-line {
display: flex;
padding: 16px 24px;
&.header {
background-color: #F5F5F5;
}
&:nth-child(n+2) {
border-top: 1px solid #D9DBDE;
}
.product-id {
width: 40px;
}
.product-name {
width: 630px;
}
}
}
}
}
改成上述的方式後,讓我們來檢視一下畫面(使用 ng serve-> http://localhost:4200/products 進行存取)。
讲解
产品列表组件.ts
在ngOnInit()中,使用setTimeout使得在3秒后数据被添加到products中。
产品列表.component.html
通过使用指令 *ngIf ,我们可以区分商品在获取之前和获取之后的显示方式。
<div class="waiting-for-products" *ngIf="products === null; else productList">
如果products为空,则显示该div,否则显示包含在productList中的元素。在product-list.component.ts中,由于在组件初始化3秒后,products不再为空,因此只有在最开始的3秒钟才会显示。
<ng-template #productList>
在 *ngIf 的 else productList 部分指定的是上述部分。
这个 else 部分,请使用 ng-template 元素。此外,在其中要用带 # 的方式指定 productList 。
这样,该元素将在3秒后显示出来。
提示:指令是什么
指令可以被理解为类似于HTML属性的东西,可以为组件添加特定功能的模块。
我通常会创建一个指令,可以在只写一个的情况下同时写入src和srcset,因为我觉得一次写两个很麻烦。
实际提交的情况
Angular 的指令基础(通过 *ngFor 在 HTML 中进行循环)
在上述示例中,将所有三种商品都在HTML中分别列出。然而,这种方法需要确定商品数量,而且非常麻烦。
在Angular中,有一个指令*ngFor,可以用来重复(循环)编写HTML标签。
我会使用这个来重写代码。
<div class="container">
<div class="title">商品一覧</div>
<div class="product-list-container">
<div class="waiting-for-products" *ngIf="products === null; else productList">
商品を取得しています...
</div>
<ng-template #productList>
<div class="product-list-table">
<div class="product-line header">
<div class="product-id">ID</div>
<div class="product-name">名前</div>
<div class="product-price">価格</div>
</div>
<div class="product-line" *ngFor="let product of products">
<div class="product-id">{{ product.id }} </div>
<div class="product-name">{{ product.name }}</div>
<div class="product-price">{{ product.price }}</div>
</div>
</div>
</ng-template>
</div>
</div>
*ngFor 的基本用法是使用 “let xxx of yyy” 的方式进行声明。这个语法是指将 yyy 中的每个元素赋值给变量 xxx,然后进行循环操作。
这个用法与常规的 JavaScript 中的循环语句(for)写法几乎相同,因此应该很容易理解。
提示:ngFor的一些变量
在ngFor中,我们可以轻松地获取循环中常用的索引、是否是第一个或最后一个等信息。
例如:
- 这样做,索引值会被赋给 i,第一个元素的布尔值会被赋给 f。非常方便!
更多详细信息请看这里↓
https://angular.io/api/common/NgForOf#local-variables
这个提交到目前为止
赠品
类似于 *ngIf 的是 ngSwitch。
根据名称,这是一个可以使用开关语句的内容。
作为使用方法
<div [ngSwitch]="state">
<div *ngSwitchCase="1">One</div>
<div *ngSwitchCase="2">Two</div>
<div *ngSwitchDefault>Default</div>
</div>
在使用时,可以考虑使用这个选项。如果有很多分支的情况,也可以选择使用这个。
总结
本次我们学习了在项目中进行数据交换的模型构建方法,以及使用 *ngIf 进行条件分支和 *ngFor 进行循环。你可能已经开始逐渐理解 Angular 了。
下一步,我们将解释Typescript的基本部分,并解释在Angular中如何将数据管理与组件分离。Angular入门系列第五部分:在一个月内让初学者能够创建服务与掌握Typescript基础。
入门文章目录
「Angular入门 从零开始,一个月内学会创建服务」由于文章数量较多,正在编写概括文章。