[Angular] Day13. Built-in directives - attribute

在介绍完 component 与 template 後,接着要来介绍什麽是 dierctive,可能在前面的章节中多多少少都有提到,但是可能都没有详细的讲解过,也有遇过实际上已经有在使用了但却不知道这个其实就是 directive,不过没关系接下来会详细的介绍什麽是 directive。

directive 是 Angular 中为了应用程序元素额外添加行为的 class,可以使用 Angular 内建的 directive 来管理 formlistsstyle使用者看到的内容

而 directive 有不同类性的用法,以下分为三种:

  1. Component: 带有 template 的 directive,这种是最常见的 directive
  2. Attribute directive: 改变 element, component 或其他 directive 的外观或行为的 directive
  3. Structureal directive: 通过添加或删除 DOM element 来改变 DOM 的布局

https://ithelp.ithome.com.tw/upload/images/20210807/20124767SxBTFn4QZA.png


Built-in attribute directives

attribute directive 是用来监听和修改 HTML 的 element、attribute、property 和 component 的行为,比如 RouterModule 或 FormsModule 这类型的 NgModule,他都有定义自己的 attribute directive,而常见的的 attribute directive 有:

  • NgClass: 添加或删除一组 css class
  • NgStyle: 添加或删除一组 HTML style
  • NgModule: 像 HTML 的 <form> element 添加双向数据绑定

Adding and removing classes with NgClass

可以使用 ngClass 同时添加或删除一个或多个 CSS class,而如果要添加或删除一个 CSS class 则不要使用 ngClass,请使用 class binding。

Using NgClass with an expression

要在设置样式的 element 上添加 [ngClass] 并将一个表达式赋予给他,来举个子吧

<div [ngClass]="isSpecial ? 'special' : ''">This div is special</div>

在上面的例子中可以看到将一个三元运算子作为表达式赋予给 [ngClass],所以当 isSpecial 为 true 时,就会在这个 <div> element 中套用名为 special 的 CSS class 反之则不套用任何 class。

Using NgClass with a method

除了将表达式赋予给 [ngClass] 之外,也可以与 component 中的 method 一起使用,举个例子吧,希望透过画面中三个 check box 来更换画面中的字体或颜色。

  1. 在 app.component.html 中建立三个 chech box 与一个按钮

    <!-- app.component.html -->
    
    <div [ngClass]="currentClasses">
      This div element CSS class is initial color, size and bgcColor
    </div>
    
    <ul>
      <li>
        <label for="colorChange">Change color</label>
        <input type="checkbox" id="colorChange" (change)="onChange('color')" />
      </li>
      <li>
        <label for="fontSizeChange">Font size change</label>
        <input type="checkbox" id="fontSizeChange" (change)="onChange('size')" />
      </li>
      <li>
        <label for="bgcChange">Background color change</label>
        <input type="checkbox" id="bgcChange" (change)="onChange('bgcColor')" />
      </li>
    </ul>
    
    <button (click)="onSetCurrentClasses()">Change div CSS class</button>
    
    • (1): 在最上面添加一个 <div> 并将他使用 [ngClass]="currentClasses",透过 currentClasses 的内容决定要将几个 CSS class 绑定在上面
    • (2): 新增三个 check box 并将他们绑定一个 method,当使用者点击时改变他们各自的状态
    • (3): 当使用者选择好 check box 後,点击按钮触发重新设置 currentClasses
  2. 在 app.component.ts 中新增 property 与 method

    import { Component } from '@angular/core';
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
    })
    export class AppComponent {
      color = false;                                      // (1)
      size = false;
      bgcColor = false;
      currentClasses = { color: false, size: false, bgcColor: false };
    
      onChange(checkboxId: string) {                      // (2)
        switch (checkboxId) {
          case 'color':
            this.color = !this.color;
            break;
          case 'size':
            this.size = !this.size;
            break;
          case 'bgcColor':
            this.bgcColor = !this.bgcColor;
            break;
          default:
            break;
        }
      }
    
      onSetCurrentClasses() {                             // (3)
        this.currentClasses = {
          color: this.color,
          size: this.size, 
          bgcColor: this.bgcColor
        }
      }
    }
    
    • (1): 新增每个 check box 所控制的 property,并新增 currentClasses 用於改变整体样式
    • (2): 新增当 check box 被点击时触发的 method
    • (3): 新增当画面中的 button 被点击时触发的 method
  3. 在 app.component.css 中新增三个 CSS class

    .color {
        color: lightskyblue;
    }
    
    .size {
        font-size: 36px;
    }
    
    .bgcColor {
        background-color: lightpink;
    }
    

img

在画面中可以看到,当我们点选一个或多个 check box 时,会依照对应的名称对 <div> 添加或删除一个或多个 CSS class,当点击其中一个 check box 先将他的对应的 property 变更状态,等全部都变更好後,当使用者点击了 change 的按钮,会将 [ngClass] 所绑定的 object 进行更新从而更新 <div> 中的 CSS class。

还记得为什麽绑定物件时可以更改 CSS class 的绑定吗?在前几篇的 class binding 中有提到,可以使用物件绑定,他的要求是 key 为 class 名称, value 为布林值(用於控制是否呈现) 的这种型别的 object,忘记的可以回去复习一下喔!

Setting inline styles with NgStyle

讲完了 NgClass 後接着来介绍 NgStyle 的用法,他与 NgClass 差不多,唯一不同的是他是可以添加或删除一个或多个 style,既然他们的使用方法差不多,那我们就以上面的例子,将上面的例子从 NgClass 更改为 NgStyle 吧。

  1. 首先先将 app.component.html 中显示的 <div> 从 [ngClass] 更改为 [ngStyle] 并将绑定的名称更改为 currentStyle,其他我们保持不变

    <!-- app.component.html -->
    
    <!-- Change [ngClass] to [ngStyle] -->
    <div [ngStyle]="currentStyle">
      This div element style is initial color, size and bgcColor
    </div>
    
    <ul>
      <li>
        <label for="colorChange">Change color</label>
        <input type="checkbox" id="colorChange" (change)="onChange('color')" />
      </li>
      <li>
        <label for="fontSizeChange">Font size change</label>
        <input type="checkbox" id="fontSizeChange" (change)="onChange('size')" />
      </li>
      <li>
        <label for="bgcChange">Background color change</label>
        <input type="checkbox" id="bgcChange" (change)="onChange('bgcColor')" />
      </li>
    </ul>
    
    <!-- Change method name -->
    <button (click)="onSetCurrentStyle()">Change div Style</button>
    
  2. 接着我们改写 app.component.ts 中的 property name 与 method

    import { Component } from '@angular/core';
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
    })
    export class AppComponent {
      color = false;
      size = false;
      bgcColor = false;
      currentStyle: Record<string, string> = {};    // (1)
    
      onChange(checkboxId: string) {
        switch (checkboxId) {
          case 'color':
            this.color = !this.color;
            break;
          case 'size':
            this.size = !this.size;
            break;
          case 'bgcColor':
            this.bgcColor = !this.bgcColor;
            break;
          default:
            break;
        }
      }
    
      onSetCurrentStyle() {                        // (2)
        this.currentStyle = {
          'color':  this.color ? 'lightskyblue' : '',
          'font-size': this.size ? '36px' : '',
          'background-color': this.bgcColor? 'lightpink': ''
        }
      }
    }
    
    • (1): 将原本的 currentClasses 改名为 currentStyle (因为已经将 <div> 重新使用 [ngStyle] 绑定过了)
    • (2): 更改使用者点击按钮时处发的 method

img

在画面中可以看到虽然将 <div> 重新使用 [ngStyle] 绑定过但是结果与使用 [ngClass] 一样,唯一不同的是在 onSetCurrentStyle 中的操作改了,如果是要绑定 style 的话需要使用以样式名称为 key,以样式值为 value 的物件 一样在前几天的 style binding 里面有提到喔!


Displaying and updating properties with ngModel

介绍完如何透过 [ngClass] 与 [ngStyle] 改变 HTML element 的样式後,接着介绍如何使用 ngModel

ngModel directive 是用在显示数据 property 与当用户对被绑定的 HTML element 进行更改时同步更新 property, 一样举个例子吧

  1. 首先要使用 ngModel 必须要在 app.module.ts 中引用 FormsModule 并将他放在 import 当中,可能很多新手会觉得为什麽要放在这边,没关系之後再讲解 Module 时会专门为大家讲解

    import { NgModule } from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    
    import { AppRoutingModule } from './app-routing.module';
    import { AppComponent } from './app.component';
    import { FormsModule } from '@angular/forms';
    
    @NgModule({
      declarations: [
        AppComponent,
      ],
      imports: [
        BrowserModule,
        AppRoutingModule,
        FormsModule
      ],
      providers: [],
      bootstrap: [AppComponent]
    })
    export class AppModule { }
    
  2. 在 app.component.ts 中新增一个 property 用於绑定画面中的 <input>

    import { Component } from '@angular/core';
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent {
      currentItem = '';
    }
    
  3. 在 app.component.html 中新增一个 <input> 并将他绑定

    <!-- app.component.html -->
    
    <label for="example-ngModel">[(ngModel)]: </label>
    <input [(ngModel)]="currentItem" id="example-ngModel">
    
    <div>{{currentItem}}</div>
    

img

在画面中可以看到当我们在 <input> 中输入数值时,会同步的将 component 中的 property 更改。

可能有人会问:阿这个跟 Two-way binding 有什麽不一样?这个答案我可以很明确地告诉你,他们是一样的,所以你也可以把它理解为在同一层中的 Two-way binidng,於是你也可以利用 property binding 与 event binding 达到相同的结果

<input [currentItem]="currentItem" (change)="onChnge($event)" id="example-ngModel">

<div>{{currentItem}}</div>

有兴趣的可以尝试看看把他从 ngModel 改写为 property binding 与 event binding 喔!


结论

本章中介绍了什麽是 attribute directive 与该怎使用,可以利用 ngClass 对 HTML 的 element 进行一个或多个的 CSS class 绑定,也可以使用 ngStyle 对 HTML 的 element 进行一个或多个的 Style 绑定,最後介绍的是 ngModel 的绑定,在使用他之前需要在 app.module.ts 的 imports 中引入 FormsModule 才可以使用,他的作用可以把它想像成是在同一层中的 Two-way binding,对於表单的控制非常好用,本章中使用了比较多例子,如果在尝试的时候遇到问题也欢迎在底下留言。

虽然本章的开头提到 Angular 的预设有 attribute 与 structural directives 两种,但碍於篇章长度的问题将它分为本篇的 attribute directive 与下一篇的 structural directive。

structural directive 的概念其实我们在前面的例子中多多少少都有用到一点,比如 *ngFor*ngIf 等等的,主要的目的是用来改变 DOM 的结构与布局,详细的内容会在明天介绍,那我们就明天见吧!


Reference


<<:  JavaScript Day 4. ParseInt / ToString

>>:  Day01 - 人工智慧遇上语音辨识

[Day 8] 整合 Koin DI 实作 Ktor Plugin

Ktor Plugin & DSL Ktor 的架构设计是让开发者透过实作 plugin,把...

Day 15 : 特徵工程 tf.Tramsform 介绍

特徵工程是机械学习相当重要的一环,有处理数据以及实行 ML/DL 任务经验者对特徵工程一定不陌生,...

[Day 08] 简单的单元测试实作(二)

接下来我们做第二个测试,我们传入一个数字, 譬如说我们传入4, 因为它是4的倍数, 所以应该是闰年,...

Day 2 - API 文件导览、 Postman 测试取得 Nonce

在进行串接前,首先需要有定义串接的规格,例如:串接的协定 (HTTP、或走 FTP 档案交换等等)、...

Flutter基础介绍与实作-Day28 旅游笔记的实作(9)

今天就继续来做剩下的两个地区吧! 一样先在assets资料夹内的View资料夹里建立南部和东部的资料...