Truyền dữ liệu giữa các Component
- 20-12-2023
- Toanngo92
- 0 Comments
Chúng ta cần tạo một component mà nó có thể nhận vào các properties. Chúng ta sẽ dùng lệnh để generate 1 component tên là progress-bar như đã học ở bài trước đó. Đoạn mã dưới đây sẽ giúp bạn tạo ra một component:
ng generate component progress-bar
Như vậy, chúng ta đã có một component để bắt đầu.
Mục lục
@input decorator
Để tạo một thanh tiến trình (progress-bar) có thể hiển thị tiến độ, chúng ta sử dụng một công cụ trong Angular gọi là @Input decorator
.
Đầu tiên, chúng ta cần định nghĩa các thuộc tính cho thành phần (component) để nó có thể nhận giá trị từ bên ngoài. Ví dụ, chúng ta muốn có một thuộc tính progress
để biểu thị tiến độ và mặc định là 0. Bằng cách sử dụng @Input()
, chúng ta có thể khai báo các thuộc tính này:
export class ProgressBarComponent implements OnInit {
@Input() backgroundColor: string;
@Input() progressColor: string;
@Input() progress = 0;
constructor() {}
ngOnInit() {}
}
Điều quan trọng là @Input
đánh dấu rằng các thuộc tính này sẽ nhận giá trị từ bên ngoài và có thể được binding với thành phần từ một component khác. Nếu không sử dụng @Input
, Angular sẽ không nhận diện được các thuộc tính này để có thể nhận giá trị.
Khi đã định nghĩa thành phần và các thuộc tính, chúng ta có thể sử dụng component này trong mã HTML của chúng ta. Bằng cách sử dụng cú pháp [property]
để binding dữ liệu vào các thuộc tính của component, chúng ta có thể truyền giá trị cho các thuộc tính này:
<app-progress-bar
[progress]="15"
[backgroundColor]="'#9e9e9e'"
[progressColor]="'#2e8b57'"
>
</app-progress-bar>
Ở đây, chúng ta sử dụng component app-progress-bar
và truyền vào các giá trị cho các thuộc tính như progress
, backgroundColor
, và progressColor
.
Cuối cùng, để hiển thị thanh tiến trình trong component, chúng ta sử dụng mã HTML và CSS bên trong template của component:
@Component({
selector: "app-progress-bar",
template: `
<div
class="progress-bar-container"
[style.backgroundColor]="backgroundColor"
>
<div
class="progress"
[style]="{
backgroundColor: progressColor,
width: progress + '%'
}"
></div>
</div>
`,
styles: [
`
.progress-bar-container,
.progress {
height: 20px;
}
.progress-bar-container {
width: 100%;
}
`,
],
})
export class ProgressBarComponent implements OnInit {
@Input() backgroundColor: string;
@Input() progressColor: string;
@Input() progress = 0;
constructor() {}
ngOnInit() {}
}
Trong template của component, chúng ta sử dụng CSS để tạo ra thanh tiến trình với các thuộc tính như màu nền, màu tiến trình và chiều rộng được xác định dựa trên giá trị của progress
.
Như vậy, thông qua việc sử dụng @Input decorator
và binding thuộc tính, chúng ta có thể tạo và điều chỉnh thành phần thanh tiến trình trong Angular một cách linh hoạt và dễ dàng.
Constructor và ngOnInit trong Angular
Khi làm việc với Angular, có một số phương thức gọi là “life-cycle” mà Angular tự động gọi trong quá trình hoạt động của component. Để hiểu rõ hơn, hãy nghĩ về cuộc đời của con người. Từ khi sinh ra đến khi qua đời, con người trải qua nhiều sự kiện quan trọng như sinh nhật, kết hôn, và có những thay đổi quan trọng.
Tương tự, trong Angular, các component cũng có vòng đời của chúng. Khi một component được tạo ra và hiển thị trên màn hình, có những “sự kiện” như khởi tạo và xóa đi khỏi view diễn ra. Để quản lý các sự kiện này, Angular cung cấp các phương thức mà chúng ta có thể khai báo trong component hoặc directive.
Trong Angular, chúng ta thường gặp hai phương thức quan trọng: Constructor và ngOnInit. Nhưng chúng khác nhau như thế nào?
Constructor là một hàm tạo của một class. Khi bạn tạo một instance (thể hiện) của class đó, constructor sẽ tự động chạy một lần duy nhất.
Trái lại, ngOnInit là một phương thức trong “life-cycle” của Angular. Nó được tự động gọi khi component được khởi tạo, sau khi constructor chạy và sau khi các giá trị đầu vào (inputs) đã được binding. Điều này có nghĩa là nếu bạn binding một giá trị trong template của component cha, thì trong constructor của component con, giá trị đó chưa được nhận. Nhưng khi ngOnInit được gọi, bạn đã có thể sử dụng giá trị đó.
Thực tế, Angular khuyến nghị rằng hãy hạn chế việc viết code logic quá nhiều trong constructor. Constructor nên thực hiện ít công việc nhất có thể và để ngOnInit xử lý các công việc còn lại.
Thay đổi giá trị của Input
Đôi khi, khi chúng ta nhận được dữ liệu từ người dùng, không phải lúc nào chúng cũng đảm bảo hợp lệ. Điều này có thể xảy ra khi người dùng truyền dữ liệu không nhất quán hoặc không phù hợp với loại dữ liệu mong đợi. Để đảm bảo rằng dữ liệu đầu vào luôn đúng đắn, chúng ta cần xác thực nó.
Một cách để làm điều này trong Angular là sử dụng lifecycle hook ngOnChanges
. Khi bất kỳ input nào bị thay đổi, ngOnChanges
sẽ được kích hoạt và cho phép chúng ta kiểm tra các thay đổi này.
Ví dụ, giả sử chúng ta có một thành phần ProgressBar:
import { Component, OnInit, OnChanges, SimpleChanges, Input } from '@angular/core';
@Component({
selector: 'app-progress-bar',
templateUrl: './progress-bar.component.html',
styleUrls: ['./progress-bar.component.css']
})
export class ProgressBarComponent implements OnInit, OnChanges {
@Input() backgroundColor: string;
@Input() progressColor: string;
@Input() progress = 0;
constructor() {}
ngOnChanges(changes: SimpleChanges) {
if ("progress" in changes) {
if (typeof changes["progress"].currentValue !== "number") {
const progress = Number(changes["progress"].currentValue);
if (Number.isNaN(progress)) {
this.progress = 0;
} else {
this.progress = progress;
}
}
}
}
ngOnInit() {}
}
Trong ví dụ này, chúng ta sử dụng ngOnChanges
để kiểm tra nếu giá trị progress
được binding thay đổi. Nếu giá trị này không phải là kiểu số, chúng ta cố gắng chuyển đổi nó thành số và kiểm tra xem liệu nó có phải là một số hợp lệ hay không. Nếu không, chúng ta gán giá trị mặc định là 0.
Ngoài cách sử dụng ngOnChanges
, một phương pháp khác là sử dụng getter và setter để xử lý giá trị của progress
. Bằng cách này, chúng ta có thể kiểm tra và xác thực giá trị mỗi khi có sự thay đổi:
import { Component, OnInit, Input } from '@angular/core';
@Component({
selector: 'app-progress-bar',
templateUrl: './progress-bar.component.html',
styleUrls: ['./progress-bar.component.css']
})
export class ProgressBarComponent implements OnInit {
@Input() backgroundColor: string;
@Input() progressColor: string;
private $progress = 0;
@Input()
get progress(): number {
return this.$progress;
}
set progress(value: number) {
if (typeof value !== "number") {
const progress = Number(value);
if (Number.isNaN(progress)) {
this.$progress = 0;
} else {
this.$progress = progress;
}
} else {
this.$progress = value;
}
}
constructor() {}
ngOnInit() {}
}
Trong ví dụ này, chúng ta sử dụng getter và setter để kiểm tra giá trị progress
mỗi khi nó được thiết lập. Nếu giá trị không phải là số, chúng ta cố gắng chuyển đổi nó và xác thực trước khi gán vào biến $progress
.
Bằng cách này, chúng ta có thể đảm bảo rằng giá trị đầu vào luôn hợp lệ và phù hợp với loại dữ liệu mong đợi.