[Tìm hiểu Angular 7] Làm quen với component trong Angular
Bài đăng này đã không được cập nhật trong 4 năm
Working With Arrays
Ở bài trước Những thành phần cơ bản chúng ta đã thử tạo ra một UserItemComponent
mới, để nói "Hello" tới một user. Nếu giờ chúng ta muốn nói "Hello" tới một list user's name thì phải làm thế nào? Trong Angular, chúng ta có thể lặp một list các đối tượng trong template bằng cách dùng cú pháp *ngFor
. Ý tưởng ở đây là chúng ta sẽ lặp cùng một kiểu cho một tập hợp các object.
Nếu bạn đã làm việc với AngularJS 1.X trước đó, thì có thể bạn đã sử dụng
ng-repeat
directive.NgFor
cũng hoạt động tương tự như vậy.
Giờ thì chúng ta sẽ tạo ra một component mới để render ra một list các user.
$ ng generate component user-list
CREATE src/app/user-list/user-list.component.css (0 bytes)
CREATE src/app/user-list/user-list.component.html (28 bytes)
CREATE src/app/user-list/user-list.component.spec.ts (643 bytes)
CREATE src/app/user-list/user-list.component.ts (280 bytes)
UPDATE src/app/app.module.ts (598 bytes)
Giờ chúng ta sẽ thay thế thẻ <app-user-item>
bằng thẻ <app-user-list>
trong fileapp.component.html
src/app/app.component.html
<h1>
{{title}}
<app-hello-world></app-hello-world>
<app-user-list></app-user-list>
</h1>
Cũng giống như cách chúng ta đã thêm property name
cho UserItemComponent
, giờ chúng ta sẽ thêm property names
cho UserListComponent
.
Tuy nhiên, thay vì chỉ lưu trữ một string duy nhất, chúng ta sẽ lưu trữ kiểu của property này là một mảng các string. Một mảng được ký hiệu bởi ký tự [] , và code cụ thể sẽ trông như thế này:
src/app/user-list/user-list.component.ts
export class UserListComponent implements OnInit {
names: string[];
constructor() {
this.names = ['Ari', 'Carlos', 'Felipe', 'Nate'];
}
ngOnInit() {
}
}
Thay đổi đầu tiên là chỉ ra một string[]
property mới trong UserListComponent
class của chúng ta. Cú pháp này có nghĩa là names là được định kiểu là một mảng của các strings
. Một cách viết khác đó là Array<string>
Chúng ta đã thay đổi constructor
để set value cho this.names
thành['Ari', 'Carlos', 'Felipe', 'Nate'].
Bây giờ chúng ta có thể update template để render ra một list các name. Để làm được điều này chúng ta sẽ dùng *ngFor
như sau:
- Lặp qua một list các item
- Generte một thẻ mới cho mỗi item.
Template mới của chúng ta sẽ trông như thế này:
src/app/user-list/user-list.component.html
> <ul>
> <li *ngFor="let name of names">Hello {{name}}</li>
</ul>
Chúng ta đã cập nhật template với một ul
và một il
với một attribute là *ngFor="let name of names"
. Lúc đầu, ký tự *
và cú pháp let
có vẻ hơi khó hiểu, vậy thì chúng ta hãy cùng tìm hiểu nó:
Cú pháp*ngFor
nói rắng chúng ta muốn sử dụng NgFor directive trên attribute
này. Bạn có thể nghĩ NgFor
giống như một vòng lặp for.
Ý tưởng ở đây là chúng ta sẽ tạo ra một DOM element mới cho mỗi item trong collection.
Trong câu lệnh"let name of names"
. Thì names
là một array đã được chỉ ra trong UserListComponent
trước đó. Và let name
được gọi là một reference
. Khi chúng ta viết "let name of names"
có nghĩ là chúng ta sẽ lặp qua từng phần tử trong names
và hiển thị mỗi biến local gọi là name.
NgFor
directive sẽ render ra một thẻ li
cho mỗi elemt được tìm thấy trong array names
. Và khai báo một local variable name
để giữ giá trị của item hiện tại được lặp qua. Và variable này sẽ được thay thế trong đoạn Hello {{ name }}
.
Chúng ta không nhất thiết phải đặt tên biến là
name
. Chúng ta có thể viết như sau:<li *ngFor="let foobar of names">Hello {{ foobar }}</li>
Ngược lại, nếu chúng ta viết như thế này thì điều gì sẽ xảy ra?
<li *ngFor="let name of foobar">Hello {{ name }}</li>
Câu trả lời là sẽ xuất hiện lỗi bởi vì
foobar
không phải là một property tồn tại trong component của chúng ta.
Khi reload lại browser, chúng ta sẽ có một thẻ li
cho mỗi string trong array.
Using the User Item Component
Bạn có nhớ là trước đó chúng ta đã cùng nhau tạo UserItemComponent
không?
Thay vì render ra mỗi name
trong UserListComponent
chúng ta nên sử dụng UserItemComponent như là child component (Component con) - nghĩa là, thay vì render một cách trực tiếp text Hello
và name, chúng ta nên để UserItemComponent
chỉ định template ( và chức năng) cho mỗi item trong list.
Để làm được điều này, chúng ta cần làm 3 điều:
- Configure
UserListComponent
để render raUserItemComponent
(Trong template). - Configure
UserItemComponent
để chấp nhậnname
variable như mộtinput
. - Configure
UserListComponent
template để truyềnname
choUserItemComponent
Nào, chúng ta hãy cũng nhau thực hiện từng bước một!
Rendering the UserItemComponent
UserItemComponent
có chỉ định selector app-user-item
- Nào, hãy thử add tag đó vào template:
src/app/user-list/user-list.component.html
<ul>
<li *ngFor="let name of names">
<app-user-item></app-user-item>
</li>
</ul>
Chú ý rằng tôi đã hoán đổi text Hello
và name thành thẻapp-user-item
. Giờ thỉ reload lại browser, để xem chúng ta thấy gì:
Nó lặp đi lặp lại, tuy nhiên có điều gì đó sai sai ở đây. Với mỗi name
đều hiển thị là "Felipe"! Chúng ta cần một cách để truyền data vào trong child component
.
Thật may mắn, Angular cung cấp một cách để làm điều này, đó là: @Input
decorator.
Accepting Inputs
Hãy nhớ rằng ở UserItemComponent
, chúng ta đã setthis.name = 'Felipe'
trong constructor. Bây giờ chúng ta cần thay đổi một chút ở component này để nó chấp nhận một value cho property name.
Và đây là những gì chúng ta cần thay đổi:
src/app/user-item/user-item.component.ts
import { Component, OnInit, Input } from '@angular/core';
@Component({
selector: 'app-user-item',
templateUrl: './user-item.component.html',
styleUrls: ['./user-item.component.css']
})
export class UserItemComponent implements OnInit {
@Input() name: string // <-- added Input annotation
constructor() {
// removed setting name
}
ngOnInit() {
}
}
Chú ý rằng, tôi đã thay đổi name
property để có thêm @Input
decorator. Chúng ta sẽ nói nhiều hơn về Inputs
(và cả Outputs
) trong chapter tiếp theo, tuy nhiên giờ chúng ta cứ biết rằng cú pháp này cho phép chúng ta truyền một value từ parent template
(template cha).
Để sử dụng Input
chúng ta phải add nó vào list hằng số trong import
.
Cuối cùng, chúng ta sẽ không set giá trị mặc định cho biến name
, nên sẽ xóa phần set đó trong constructor
.
Vậy là giờ chúng ta đã có một name Input
. Để sử dụng nó thì chúng ta phải làm thế nào?
Passing an Input value
Để truyền một value tới một component chúng ta dùng dấu ngoặc vuông [] ở trong template, cụ thể như sau:
src/app/user-list/user-list.component.html
<ul>
<li *ngFor="let name of names">
<app-user-item [name]="name"></app-user-item>
</li>
</ul>
Chú ý rằng, tôi ở thẻ app-user-item
, tôi đã thêm một attribute mới, đó là : [name]="name"
. Trong Angular khi chúng ta add thêm một attribute ở trong ngoặc như là [foo]
- nghĩa là, chúng ta muốn nói rằng, hãy truyền một value tới Input
có tên foo
trong component.
Còn trong trường hợp này, lưu ý rằng, name
ở phía bên phải là đến từ câu lệnh let name...
trong ngFor. Nếu chúng ta viết thành như thế này:
<li *ngFor="let individualUserName of names">
<app-user-item [name]="individualUserName"></app-user-item>
</li>
Phần [name]
là chỉ định Input ở trong UserItemComponent
. Chú ý rằng, ở đây chúng ta không truyền chuỗi ký tự "individualUserName" và chúng ta đang truyền vào giá trị của individualUserName
- Đó là giá trị của mỗi phần tử trong names
array.
Chúng ta sẽ nói nhiều hơn về Inputs và Outputs trong chương tiếp theo. Hiện tại, điều chúng ta đã làm đó là:
- Lặp các phần tử trong
names
- Tạo một
UserItemComponent
mới cho mỗi phần tử trongnames
- Truyền vào một giá trị của
name Input
có trongUserItemComponent
Và giờ list names của chúng ta đã được hiển thị.
Xin chúc mừng! chúng ta đã cùng nhau xây dựng một ứng dụng Angular đầu tiên với các components!
Tất nhiên, ứng dụng này khá đơn giản, và tôi muốn chúng ta cùng nhau xây dựng một ứng dụng nhiều chức năng hơn nữa, ví dụ như ứng dụng voting, có khả năng tương tác với người dùng ... Tuy nhiên trước khi bắt đầu xây dựng app mới, chúng ta sẽ cùng nhau tìm hiểu về Bootstrapping trong Angular.
Bootstrapping Crash Course
Mỗi app đều có một main entry point
.
Ứng dụng này được xây dựng bằng Angular CLI (nó được xây dựng trên một công cụ có tên là Webpack). Chúng ta chạy ứng dụng này bằng cách gọi lệnh:
ng serve
ng
sẽ tìm entry point cho ứng dụng của chúng ta trong file angular.json
. Nào, hãy cũng tìm hiểu cách ng
tìm kiếm các component mà chúng ta vừa tạo.
Ở mức high level nó sẽ trông như thế này:
angular.json
sẽ chỉ định một "main" file, trong trường hợp này làmain.ts
main.ts
là entry-point của app của chúng ta, và nó sẽbootstraps
ứng dụng của chúng ta.- Quá trình bootstrap sẽ khởi động Angular module.
- Chúng ta dùng
AppModule
để bootstrap ứng dụng.AppModule
được chỉ định trongsrc/app/app.module.ts
AppModule
sẽ chỉ địnhcomponent
nào sẽ được dùng như top-level component. Trong trường hợp thì đó làAppComponent
AppComponent
có các thẻ<app-user-list>
trong template, và nó render ra list user.
HIện tại, điều mà tôi muốn focus vào đó là Angular module system: NgModule
.
Angular có một khái niệm mạnh mẽ về các modules
. Khi bạn khởi động một Angular app, bạn sẽ không khởi động trực tiếp một component, thay vào đó bạn sẽ tạo ra một NgModule
, cái đó sẽ chỉ tới component mà bạn muốn load.
Hãy xem đoạn code dưới đây:
src/app/app.module.ts
@NgModule({
declarations: [
AppComponent,
HelloWorldComponent,
UserItemComponent,
UserListComponent
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Điều đầu tiên chúng ta thấy là một @NgModule
decorator. Giống như tất cả các decorator khác, đoạn code này @NgModule( ... )
sẽ add một metadata vào class ngay sau đó (trong trường hợp này là AppModule
).
@NgModule
decorator có 4 keys: declarations, imports, providers, và bootstrap
declarations
declarations
chỉ ra các component được định nghĩa trong module này.
Và đây là một ý tưởng quan trọng trong Angular: "Bạn phải khai báo các components trong NgModule trước khi bạn có thể sử dụng chúng trong các template của mình."
Bạn có thể nghĩ NgModule
giống như một "package" và declarations
sẽ nêu rõ các components nào thuộc sở hữu bởi module này.
Bạn có thể nhận thấy rằng khi chúng ta sử dụng ng generate
, tool sẽ tự động add các components vào declarations
list. Ý tưởng ở đây là bất cứ khi nào chúng ta tạo ra một component mới, ng
tool đều giả sử rằng chúng ta muốn nó thuộc về NgModule
hiện tại.
imports
imports sẽ mô tả những dependencies
(thành phần phụ thuộc ) mà module này có. Chúng ta đang tạo ra một browser app, do đó chúng ta sẽ import BrowserModule
. Nếu Module của bạn phụ thuộc vào các Module khác, bạn liệt kê chúng ở đây.
Điểm khác nhau giữa
import
vàimports
Có thể bạn đang đặt câu hỏi đó là, sự khác nhau giữa việc import class ở đầu file và imports ở trong module đúng k?
Câu trả lời ngắn gọn là bạn sẽ đặt cái gì đó vàoNgModule’s imports
nếu bạn sẽ sử dụng nó trong các template hay cùng vớidependency injection
. Chúng ta sẽ nói vềdependency injection
ở các phần sau.
providers
providers
được sử dụng cho dependency injection. Do đó, để làm một service sẵn sàng được inject vào app của chúng ta, thì chúng ta sẽ thêm ở đây.
bootstrap
bootstrap
sẽ nói với Angular rằng, khi module này được sử dụng để bootstrap một app, thì cần load AppComponent
trước tiên.
Hết. Mời các bạn tham khảo tiếp ở các bài viết lần tới
Nguồn: https://www.ng-book.com/2/
All rights reserved