Bài viết được dịch từ: toddmotto.com

Mục lục

  • Giới thiệu
  • AngularJS
  • Stateful component binding
  • Decorator @Input vàt stateless componen
  • Bonus: tùy biến tên thuộc tính
  • Plunker
  • Bước tiếp theo

Các bài viết trong loạt bài này

  1. Khởi động ứng dụng Angular đầu tiên của bạn
  2. Tạo Angular 2+ component đầu tiên của bạn
  3. Truyền dữ liệu vào các component Angular với @Input
  4. Các event component với EventEmitter và @Output trong Angular

Giới thiệu

Hướng dẫn này sẽ đề cập cách truyền dữ liệu tới một component, chúng ta sẽ sử dụng Couter component đã xây dựng ở bài trước. Nếu bạn chưa biết cách tạo một component trong Angular, hãy đọc bài viết này trước khi bắt đầu.

AngularJS

Với những người có nền tảng AngularJS (v1.x), khái niệm này hơi giống với phương thức .component:

const counter = {
  bindings: {
    count: '<'
  },
  template: `
    <div class="counter">
      <button ng-click="$ctrl.decrement()">
        Decrement
      </button>
      <input type="text" ng-model="$ctrl.count">
      <button ng-click="$ctrl.increment()">
        Increment
      </button>
    </div>
  `,
  controller() {
    this.$onInit = () => {
      this.count = this.count || 0;
    };
    this.increment = () => {
      this.count++;
    };
    this.decrement = () => {
      this.count--;
    };
  }
};

angular
  .module('app')
  .component('counter', counter);

Thành phần chính ở đây là sử dụng đối tượng bindings để khai báo thuộc tính count như một đầu vào one-way binding (liên kết một chiều). Cách làm cũ là sử dụng scope và có lẽ bindToController với phương thức .directive().

Stateful component binding

Sử dụng khái niệm tương tự như trên với bindings, cái phụ thuộc vào dữ liệu của component cha (stateful component). Chúng ta sẽ tạo một một stateful component, nơi thiết lập một vài dữ liệu ban đầu và truyền xuống CounterComponent.

Ở bài trước, chúng ta đã đăng ký CounterComponent trong @NgModule, để cho phép sử dụng nó bên trong các component đã đăng ký của module.

Hãy chuyển tới AppComponent, chúng ta có thể khai báo CounterComponent như một custom element (phần tử tự tạo) bên trong template:

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <div class="app">
      <counter></counter>
    </div>
  `
})
export class AppComponent {
  initialCount: number = 10;
}

Bạn có thấy initialCount? Chúng ta cần liên kết nó với CounterComponent!

Chúng ta đã học về property binding trong các bài trước, và điều tương tự được áp dụng với các custom element. Điểm khác biệt là ở chỗ chúng ta cần phải nói với Angular tên thuộc tính. Hãy tạo một thuộc tính count trên CounterComponent và truyền cho nó giá trị của initialCount:

@Component({
  selector: 'app-root',
  template: `
    <div class="app">
      <counter [count]="initialCount"></counter>
    </div>
  `
})
export class AppComponent {
  initialCount: number = 10;
}

@Input decorator và stateless component 

Bây giờ hãy tạo một stateless hay "dumb" component (component con), để truyền dữ liệu tới nó, cái sẽ biến đổi và đẩy dữ liệu ra ngoài. Chúng ta sẽ học cách đẩy dữ liệu mới ra ngoài component trong bài tiếp theo.

Hãy chuyển tới CounterComponent:

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

@Component({...})
export class CounterComponent {

  count: number = 0;

  increment() {
    this.count++;
  }

  decrement() {
    this.count--;
  }

}

Tại thời điểm này chúng ta đã có một stateless component, và cần truyền dữ liệu tới component này.

Để làm điều này, chúng ta cần import Input decorator từ Angular core, và sử dụng nó với thuộc tính count:

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

@Component({...})
export class CounterComponent {

  @Input()
  count: number = 0;

  increment() {
    this.count++;
  }

  decrement() {
    this.count--;
  }

}

Decorator này nói với Angular đối xử với count như một input binding (đầu vào), giống như cú pháp '<' của AngularJS (v1.x). Cú pháp mới dễ hơn và ngắn hơn, chúng ta quản lý bindings tại một nơi duy nhất, thay vì một đối tượng bindings như ở phần đầu bài viết này.

Tip: count: number = 0, để thiết lập giá trị mặc định cho count, nếu không có dữ liệu truyền vào, nó sẽ khởi tạo count với giá trị là 0.

Bonus: tùy biến tên thuộc tính

Có thể bạn muốn tên thuộc tính công khai (public) khác với tên thuộc tính ở bên trong component. Như thế này:

@Component({
  selector: 'app-root',
  template: `
    <div class="app">
      <counter [init]="initialCount"></counter>
    </div>
  `
})
export class AppComponent {
  initialCount: number = 10;
}

Bạn có thể thấy chúng ta đã thay đổi [count] thành [init], vậy điều này sẽ ảnh hưởng như thế nào tới input binding bên trong CounterComponent? Hiện tại, điều này sẽ lỗi:

@Component({
  selector: 'app-root',
  template: `
    <div class="app">
      <counter [init]="initialCount"></counter>
    </div>
  `
})
export class AppComponent {
  initialCount: number = 10;
}

Tại sao lại như vậy? Bởi vì count không còn được liên kết, thay vào đó chúng ta đang liên kết tới thuộc tính init. Để giữ tên thuộc tính bên trong khác với thuộc tính công khai, chúng ta có thể làm thế này:

@Component({...})
export class CounterComponent {

  @Input('init')
  count: number = 0;

  // ...

}

Truyền một chuỗi vào @Input() decorator với tên của thuộc tính chúng ta muốn liên kết. Đó là cách, chúng ta có thể sử dụng this.count trong CounterComponent như bình thường.

Plunker

Mọi thứ chúng ta làm đã có sẵn trong một Plunker:

Bước tiếp theo

Làm thế nào để báo hiệu khi counterValue (bên trong CounterComponent) đã thay đổi? Thay vì @Input, chúng ta có thể sử dụng @OutputEventEmiter - hãy khám phá trong bài tiếp theo.