Angular入门 从零开始,一个月内能够制作一个服务的能力 之四:模型、循环和分支

上一次,我们创建了多个页面并尝试了页面跳转,从而学习了路由的基础。

本文将讨论

    • Angularでのデータ管理の基礎

モデルクラスの作成

Angularのディレクティブ基礎

HTMLでの分岐
HTMLでのループ

我会继续做下去。

这篇文章的源代码

这是一个关于Angular Guides的Github页面,地址是https://github.com/seteen/AngularGuides/tree/入門その04。

Angular数据管理的基础

上次,我创建了product-list.component和product-detail.component。
在之前,这些名称并没有特别的意义,但是从这次开始,我想要给它们赋予含义。

コンポーネント名役割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。

001.png

应该在屏幕上显示产品的信息。

解释

尽管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)。

002.png

当获取到数据时,使其显示为不同的形式。

接下来,假设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 進行存取)。

003.gif

讲解

产品列表组件.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入门 从零开始,一个月内学会创建服务」由于文章数量较多,正在编写概括文章。

广告
将在 10 秒后关闭
bannerAds