Angular9 用戶輸入

2020-06-30 18:01 更新

用戶輸入

當用戶點擊鏈接、按下按鈕或者輸入文字時,這些用戶動作都會產生 DOM 事件。 本章解釋如何使用 Angular 事件綁定語法把這些事件綁定到事件處理器。

綁定到用戶輸入事件

你可以使用 Angular 事件綁定機制來響應任何 DOM 事件。 許多 DOM 事件是由用戶輸入觸發(fā)的。綁定這些事件可以獲取用戶輸入。

要綁定 DOM 事件,只要把 DOM 事件的名字包裹在圓括號中,然后用放在引號中的模板語句對它賦值就可以了。

下例展示了一個事件綁定,它實現(xiàn)了一個點擊事件處理器:

Path:"src/app/click-me.component.ts"

<button (click)="onClickMe()">Click me!</button>

等號左邊的 (click) 表示把按鈕的點擊事件作為綁定目標。 等號右邊引號中的文本是模板語句,通過調用組件的 onClickMe 方法來響應這個點擊事件。

寫綁定時,需要知道模板語句的執(zhí)行上下文。 出現(xiàn)在模板語句中的每個標識符都屬于特定的上下文對象。 這個對象通常都是控制此模板的 Angular 組件。 上例中只顯示了一行 HTML,那段 HTML 片段屬于下面這個組件:

Path:"src/app/click-me.component.ts"

@Component({
  selector: 'app-click-me',
  template: `
    <button (click)="onClickMe()">Click me!</button>
    {{clickMessage}}`
})
export class ClickMeComponent {
  clickMessage = '';


  onClickMe() {
    this.clickMessage = 'You are my hero!';
  }
}

當用戶點擊按鈕時,Angular 調用 ClickMeComponentonClickMe 方法。

通過 $event 對象取得用戶輸入

DOM 事件可以攜帶可能對組件有用的信息。 本節(jié)將展示如何綁定輸入框的 keyup 事件,在每個敲擊鍵盤時獲取用戶輸入。

下面的代碼監(jiān)聽 keyup 事件,并將整個事件載荷 ($event) 傳給組件的事件處理器。

Path:"src/app/keyup.components.ts (template v.1)"

template: `
  <input (keyup)="onKey($event)">
  <p>{{values}}</p>
`

當用戶按下并釋放一個按鍵時,觸發(fā) keyup 事件,Angular 在 $event 變量提供一個相應的 DOM 事件對象,上面的代碼將它作為參數(shù)傳給 onKey() 方法。

Path:"src/app/keyup.components.ts (class v.1)"

export class KeyUpComponent_v1 {
  values = '';


  onKey(event: any) { // without type info
    this.values += event.target.value + ' | ';
  }
}

$event 對象的屬性取決于 DOM 事件的類型。例如,鼠標事件與輸入框編輯事件包含了不同的信息。

所有標準 DOM 事件對象都有一個 target 屬性, 引用觸發(fā)該事件的元素。 在本例中,target 是 <input> 元素, event.target.value 返回該元素的當前內容。

在組件的 onKey() 方法中,把輸入框的值和分隔符 (|) 追加組件的 values 屬性。 使用插值來把存放累加結果的 values 屬性回顯到屏幕上。

假設用戶輸入字母“abc”,然后用退格鍵一個一個刪除它們。 用戶界面將顯示:

a | ab | abc | ab | a | |

或者,你可以用 event.key 替代 event.target.value,積累各個按鍵本身,這樣同樣的用戶輸入可以產生:

&
a | b | c | backspace | backspace | backspace |

$event的類型

上例將 $event 轉換為 any 類型。 這樣簡化了代碼,但是有成本。 沒有任何類型信息能夠揭示事件對象的屬性,防止簡單的錯誤。

下面的例子,使用了帶類型方法:

Path:"src/app/keyup.components.ts (class v.1 - typed )"

export class KeyUpComponent_v1 {
  values = '';




  onKey(event: KeyboardEvent) { // with type info
    this.values += (event.target as HTMLInputElement).value + ' | ';
  }
}

$event 的類型現(xiàn)在是 KeyboardEvent。 不是所有的元素都有 value 屬性,所以它將 target 轉換為輸入元素。 OnKey 方法更加清晰地表達了它期望從模板得到什么,以及它是如何解析事件的。

傳入 $event 是靠不住的做法

類型化事件對象揭露了重要的一點,即反對把整個 DOM 事件傳到方法中,因為這樣組件會知道太多模板的信息。 只有當它知道更多它本不應了解的 HTML 實現(xiàn)細節(jié)時,它才能提取信息。 這就違反了模板(用戶看到的)和組件(應用如何處理用戶數(shù)據(jù))之間的分離關注原則。

下面將介紹如何用模板引用變量來解決這個問題。

從一個模板引用變量中獲得用戶輸入

還有另一種獲取用戶數(shù)據(jù)的方式:使用 Angular 的模板引用變量。 這些變量提供了從模塊中直接訪問元素的能力。 在標識符前加上井號 (#) 就能聲明一個模板引用變量。

下面的例子使用了局部模板變量,在一個超簡單的模板中實現(xiàn)按鍵反饋功能。

Path:"src/app/loop-back.component.ts"

@Component({
  selector: 'app-loop-back',
  template: `
    <input #box (keyup)="0">
    <p>{{box.value}}</p>
  `
})
export class LoopbackComponent { }

這個模板引用變量名叫 box,在 <input> 元素聲明,它引用 <input> 元素本身。 代碼使用 box 獲得輸入元素的 value 值,并通過插值把它顯示在 <p> 標簽中。

這個模板完全是完全自包含的。它沒有綁定到組件,組件也沒做任何事情。

在輸入框中輸入,就會看到每次按鍵時,顯示也隨之更新了。

除非你綁定一個事件,否則這將完全無法工作。

只有在應用做了些異步事件(如擊鍵),Angular 才更新綁定(并最終影響到屏幕)。 本例代碼將 keyup 事件綁定到了數(shù)字 0,這可能是最短的模板語句了。 雖然這個語句不做什么,但它滿足 Angular 的要求,所以 Angular 將更新屏幕。

從模板變量獲得輸入框比通過 $event 對象更加簡單。 下面的代碼重寫了之前 keyup 示例,它使用變量來獲得用戶輸入。

Path:"src/app/keyup.components.ts (v2)"

@Component({
  selector: 'app-key-up2',
  template: `
    <input #box (keyup)="onKey(box.value)">
    <p>{{values}}</p>
  `
})
export class KeyUpComponent_v2 {
  values = '';
  onKey(value: string) {
    this.values += value + ' | ';
  }
}

這個方法最漂亮的一點是:組件代碼從視圖中獲得了干凈的數(shù)據(jù)值。再也不用了解 $event 變量及其結構了。

按鍵事件過濾(通過 key.enter)

(keyup) 事件處理器監(jiān)聽每一次按鍵。 有時只在意回車鍵,因為它標志著用戶結束輸入。 解決這個問題的一種方法是檢查每個 $event.keyCode,只有鍵值是回車鍵時才采取行動。

更簡單的方法是:綁定到 Angular 的 keyup.enter 模擬事件。 然后,只有當用戶敲回車鍵時,Angular 才會調用事件處理器。

Path:"src/app/keyup.components.ts (v3)"

@Component({
  selector: 'app-key-up3',
  template: `
    <input #box (keyup.enter)="onEnter(box.value)">
    <p>{{value}}</p>
  `
})
export class KeyUpComponent_v3 {
  value = '';
  onEnter(value: string) { this.value = value; }
}

下面展示了它的工作原理。

失去焦點事件 (blur)

前上例中,如果用戶沒有先按回車鍵,而是移開了鼠標,點擊了頁面中其它地方,輸入框的當前值就會丟失。 只有當用戶按下了回車鍵候,組件的 values屬性才能更新。

下面通過同時監(jiān)聽輸入框的回車鍵和失去焦點事件來修正這個問題。

Path:"src/app/keyup.components.ts (v4)"

@Component({
  selector: 'app-key-up4',
  template: `
    <input #box
      (keyup.enter)="update(box.value)"
      (blur)="update(box.value)">


    <p>{{value}}</p>
  `
})
export class KeyUpComponent_v4 {
  value = '';
  update(value: string) { this.value = value; }
}

結合使用

現(xiàn)在,在一個微型應用中一起使用它們,應用能顯示一個英雄列表,并把新的英雄加到列表中。 用戶可以通過輸入英雄名和點擊“添加”按鈕來添加英雄。

下面就是“簡版英雄指南”組件。

Path:"src/app/little-tour.component.ts"

@Component({
  selector: 'app-little-tour',
  template: `
    <input #newHero
      (keyup.enter)="addHero(newHero.value)"
      (blur)="addHero(newHero.value); newHero.value='' ">


    <button (click)="addHero(newHero.value)">Add</button>


    <ul><li *ngFor="let hero of heroes">{{hero}}</li></ul>
  `
})
export class LittleTourComponent {
  heroes = ['Windstorm', 'Bombasto', 'Magneta', 'Tornado'];
  addHero(newHero: string) {
    if (newHero) {
      this.heroes.push(newHero);
    }
  }
}

源代碼

  1. Path:"src/app/click-me.component.ts"

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


    @Component({
      selector: 'app-click-me',
      template: `
        <button (click)="onClickMe()">Click me!</button>
        {{clickMessage}}`
    })
    export class ClickMeComponent {
      clickMessage = '';


      onClickMe() {
        this.clickMessage = 'You are my hero!';
      }
    }

  1. Path:"src/app/keyup.components.ts"

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


    @Component({
      selector: 'app-key-up1',
      template: `
        <input (keyup)="onKey($event)">
        <p>{{values}}</p>
      `
    })
    export class KeyUpComponent_v1 {
      values = '';


      /*
      onKey(event: any) { // without type info
        this.values += event.target.value + ' | ';
      }
      */


      onKey(event: KeyboardEvent) { // with type info
        this.values += (event.target as HTMLInputElement).value + ' | ';
      }
    }


    //////////////////////////////////////////


    @Component({
      selector: 'app-key-up2',
      template: `
        <input #box (keyup)="onKey(box.value)">
        <p>{{values}}</p>
      `
    })
    export class KeyUpComponent_v2 {
      values = '';
      onKey(value: string) {
        this.values += value + ' | ';
      }
    }


    //////////////////////////////////////////


    @Component({
      selector: 'app-key-up3',
      template: `
        <input #box (keyup.enter)="onEnter(box.value)">
        <p>{{value}}</p>
      `
    })
    export class KeyUpComponent_v3 {
      value = '';
      onEnter(value: string) { this.value = value; }
    }


    //////////////////////////////////////////


    @Component({
      selector: 'app-key-up4',
      template: `
        <input #box
          (keyup.enter)="update(box.value)"
          (blur)="update(box.value)">


        <p>{{value}}</p>
      `
    })
    export class KeyUpComponent_v4 {
      value = '';
      update(value: string) { this.value = value; }
    }

  1. Path:"src/app/loop-back.component.ts"

    import { Component } from '@angular/core';
    @Component({
      selector: 'app-loop-back',
      template: `
        <input #box (keyup)="0">
        <p>{{box.value}}</p>
      `
    })
    export class LoopbackComponent { }

  1. Path:"src/app/little-tour.component.ts"

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


    @Component({
      selector: 'app-little-tour',
      template: `
        <input #newHero
          (keyup.enter)="addHero(newHero.value)"
          (blur)="addHero(newHero.value); newHero.value='' ">


        <button (click)="addHero(newHero.value)">Add</button>


        <ul><li *ngFor="let hero of heroes">{{hero}}</li></ul>
      `
    })
    export class LittleTourComponent {
      heroes = ['Windstorm', 'Bombasto', 'Magneta', 'Tornado'];
      addHero(newHero: string) {
        if (newHero) {
          this.heroes.push(newHero);
        }
      }
    }

Angular 還支持被動事件偵聽器。例如,你可以使用以下步驟使?jié)L動事件變?yōu)楸粍颖O(jiān)聽。

  1. 在 src 目錄下創(chuàng)建一個 "zone-flags.ts" 文件。

  1. 往這個文件中添加如下語句。

(window as any)['__zone_symbol__PASSIVE_EVENTS'] = ['scroll'];

  1. 在 "src/polyfills.ts" 文件中,導入 "zone.js" 之前,先導入新創(chuàng)建的 "zone-flags" 文件。

import './zone-flags';
import 'zone.js/dist/zone';  // Included with Angular CLI.

經過這些步驟,你添加 scroll 事件的監(jiān)聽器時,它就是被動(passive)的。

小結

  • 使用模板變量來引用元素 — newHero 模板變量引用了 <input> 元素。 你可以在 <input> 的任何兄弟或子級元素中引用 newHero。

  • 傳遞數(shù)值,而非元素 — 獲取輸入框的值并將它傳給組件的 addHero,而不要傳遞 newHero

  • 保持模板語句簡單 — (blur) 事件被綁定到兩個 JavaScript 語句。 第一句調用 addHero。第二句 newHero.value='' 在添加新英雄到列表中后清除輸入框。
以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號