+7

Angular Bài 3 - Templates và Data Binding

1. Giới thiệu

Chào các bạn, hôm nay mình sẽ chia sẻ đến các bạn về Templates và Data Binding trong Angular. Đây là những kiến thức mà mỗi lập trình viên Angular đều cần biết. Mình nghĩ đây sẽ là một bài viết cực kỳ thú vị và hữu ích dành cho các mọi người, không chỉ cho những bạn đang làm việc với Angular mà còn cho cả những bạn mới học Angular nữa nhé. Nào, cùng bắt đầu nào!

2. Templates trong Angular

2.1. Khái niệm về Templates

Templates trong Angular, đúng như cái tên của nó, là nơi mà mình định nghĩa giao diện người dùng (UI) cho ứng dụng Angular của mình. Mình cứ tưởng tượng nó như là một khuôn mẫu để định hình cho giao diện của mình vậy.

2.2. Các loại Templates

2.2.1. Inline Templates

Inline Templates, hay còn gọi là Templates nội tuyến, là templates mà mình khai báo trực tiếp trong component bằng cách sử dụng cú pháp back-tick (`). Ví dụ:

@Component({
  selector: 'app-root',
  template: `<h1>Welcome to {{title}}!</h1>`
})
export class AppComponent {
  title = 'My Angular App';
}

Ở đây, mình đã khai báo một inline template với nội dung là một thẻ h1 chào mừng người dùng đến với ứng dụng Angular của mình.

2.2.2. External Templates

Ngược lại với Inline Templates, External Templates là templates mà mình khai báo trong một file HTML riêng biệt và sau đó liên kết với component qua URL. Ví dụ:

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {
  title = 'My Angular App';
}

Ở đây, mình đã liên kết file app.component.html với component AppComponent để sử dụng như một external template.

2.3. Sử dụng biểu thức trong Templates

Mình cũng có thể sử dụng biểu thức trong templates để truy xuất và hiển thị dữ liệu từ component. Các biểu thức này có thể là các biến, hàm hoặc các phép toán cơ bản. Các biểu thức này sẽ được đặt trong dấu ngoặc nhọn ({}).

2.4. Best practice khi làm việc với Templates

Một số best practice khi làm việc với Templates:

  • Sử dụng external templates thay vì inline templates để dễ dàng quản lý và bảo dưỡng code.
  • Giữ cho templates đơn giản và dễ đọc.
  • Tránh sử dụng quá nhiều logic phức tạp trong templates.

3. Data Binding trong Angular

3.1. Khái niệm về Data Binding

Data Binding là cách để mình liên kết dữ liệu giữa model và view. Với data binding, mình có thể đảm bảo rằng view luôn cập nhật một cách tự động theo model và ngược lại.

3.2. Các loại Data Binding

3.2.1. Interpolation

Interpolation là một cách để mình truy xuất dữ liệu từ component và hiển thị lên template thông qua dấu ngoặc nhọn {}. Ví dụ:

@Component({
  selector: 'app-root',
  template: `<h1>Welcome to {{title}}!</h1>`
})
export class AppComponent {
  title = 'My Angular App';
}

Ở đây, mình đã sử dụng interpolation để hiển thị giá trị của biến title lên template.

3.2.2. Property Binding

Property Binding cho phép mình liên kết dữ liệu từ component tới một thuộc tính của một HTML element. Mình sử dụng dấu ngoặc vuông [] để thực hiện property binding. Ví dụ:

@Component({
  selector: 'app-root',
  template: `<img [src]="imageUrl">`
})
export class AppComponent {
  imageUrl = 'https://example.com/image.png';
}

Ở đây, mình đã liên kết giá trị của biến imageUrl tới thuộc tính src của thẻ img.

3.2.3. Event Binding

Event Binding cho phép mình xử lý các sự kiện người dùng (như click chuột, gõ phím,...) bằng cách gọi các hàm trong component. Mình sử dụng dấu ngoặc đơn () để thực hiện event binding. Ví dụ:

@Component({
  selector: 'app-root',
  template: `<button (click)="handleClick()">Click me!</button>`
})
export class AppComponent {
  handleClick() {
    alert('Button clicked!');
  }
}

Ở đây, mình đã định nghĩa một event binding để gọi hàm handleClick() khi người dùng click vào button.

3.2.4. Two-Way Binding

Two-way Binding là sự kết hợp giữa Property Binding và Event Binding. Nó cho phép mình cập nhật dữ liệu theo cả hai chiều: từ view tới model và từ model tới view. Mình sử dụng cú pháp [(...)] để thực hiện two-way binding. Ví dụ:

@Component({
  selector: 'app-root',
  template: `<input [(ngModel)]="name">`
})
export class AppComponent {
  name = '';
}

Ở đây, mình đã định nghĩa một two-way binding để liên kết giá trị của thuộc tính ngModel với biến name. Khi người dùng thay đổi giá trị của input, giá trị của biến name cũng sẽ thay đổi theo, và ngược lại.

4. RxJs và Data Binding

4.1. RxJs trong Angular

Trong Angular, RxJS là một thư viện được sử dụng rất nhiều, giúp mình xử lý các tác vụ bất đồng bộ và các sự kiện dựa trên programming reactive. RxJS cung cấp các công cụ như Observables, Subjects, Operators,... giúp mình xử lý và điều khiển dữ liệu một cách linh hoạt hơn.

4.2. Liên hệ của RxJs với Data Binding

Data Binding và RxJs đều giúp mình xử lý và kiểm soát dữ liệu trong ứng dụng Angular. Trong khi Data Binding giúp mình liên kết dữ liệu giữa model và view, thì RxJs lại cung cấp các công cụ giúp mình xử lý và kiểm soát dữ liệu một cách linh hoạt hơn.

Ví dụ về việc kết hợp Data Binding và RxJs trong Angular:

@Component({
  selector: 'app-root',
  template: `<div *ngIf="data$ | async as data">{{data}}</div>`
})
export class AppComponent {
  data$ = of('Hello, World!');
}

Ở đây, mình sử dụng async pipe (một feature của RxJs) để subscribe tới data$ Observable và tự động update view mỗi khi có dữ liệu mới.

4.3. Ví dụ:

Dưới đây là một ví dụ trực quan về việc sử dụng RxJs và Data Binding trong Angular. Ở đây, mình sẽ tạo một service để lấy dữ liệu từ một API, sau đó sử dụng pipe để biến đổi dữ liệu và cuối cùng là binding dữ liệu vào template.

my.service.ts

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class MyService {
  constructor(private http: HttpClient) { }

  getData(): Observable<any> {
    return this.http.get('https://example.com/api/data');
  }
}

my.component.ts

import { Component, OnInit } from '@angular/core';
import { MyService } from './my.service';
import { map } from 'rxjs/operators';

@Component({
  selector: 'app-my',
  template: `
    <div *ngIf="data$ | async as data">
      <div *ngFor="let item of data">{{item.name}}</div>
    </div>
  `
})
export class MyComponent implements OnInit {
  data$;

  constructor(private myService: MyService) { }

  ngOnInit() {
    this.data$ = this.myService.getData().pipe(
      map(data => data.filter(item => item.isActive))
    );
  }
}

Trong ví dụ trên, mình đã tạo một service MyService để lấy dữ liệu từ một API. Sau đó, trong component MyComponent, mình sử dụng pipe để lọc ra những item đang active. Cuối cùng, mình sử dụng async pipe và directive ngFor để binding dữ liệu vào template.

5. Kết luận

Templates và Data Binding là hai khái niệm cốt lõi trong Angular, giúp mình xây dựng giao diện người dùng (UI) và điều khiển dữ liệu trong ứng dụng. Thông qua bài viết này, mình đã tìm hiểu sâu về Templates và Data Binding, cách sử dụng chúng cũng như một số best practice khi làm việc với chúng.

Mình hy vọng sau bài viết này, các bạn đã có thêm kiến thức vững chắc về Templates và Data Binding trong Angular. Hãy cố gắng thực hành và áp dụng những kiến thức đã học vào các dự án thực tế để nắm vững hơn nhé!

5.1 Câu hỏi ôn tập

1. Khi nào nên sử dụng Inline Templates, khi nào nên sử dụng External Templates? Mình nên sử dụng Inline Templates khi template của mình đơn giản và ngắn gọn, khi mình không cần tái sử dụng template này ở nơi khác. Ngược lại, khi template phức tạp và có khả năng tái sử dụng, mình nên sử dụng External Templates.

2. Làm thế nào để thực hiện Interpolation trong Angular? Mình thực hiện Interpolation trong Angular bằng cách sử dụng dấu ngoặc nhọn {}. Ví dụ: {{title}}

3. Có những loại Data Binding nào trong Angular? Có 4 loại Data Binding trong Angular: Interpolation, Property Binding, Event Binding, và Two-Way Binding.

4. Làm thế nào để thực hiện Two-Way Binding trong Angular? Mình thực hiện Two-Way Binding trong Angular bằng cách sử dụng cú pháp [(...)]. Ví dụ: <input [(ngModel)]="name">

5. Làm thế nào để kết hợp RxJs với Data Binding trong Angular? Mình có thể kết hợp RxJs với Data Binding trong Angular bằng cách sử dụng async pipe. async pipe giúp mình subscribe tới một Observable và tự động cập nhật view mỗi khi có dữ liệu mới. Ví dụ: <div *ngIf="data$ | async as data">{{data}}</div>


English Version

1. Introduction

Hello everyone, today I'm going to share with you about Templates and Data Binding in Angular. These are the knowledge that every Angular developer needs to know. I think this will be an extremely interesting and useful article for everyone, not only for those who are working with Angular but also for those who are new to Angular. Now, let's get started!

2. Templates in Angular

2.1. Understanding Templates

Templates in Angular, as the name suggests, are where we define the user interface (UI) for our Angular application. You can imagine it as a template that shapes your interface.

2.2. Types of Templates

2.2.1. Inline Templates

Inline Templates, are templates that we declare directly in the component by using back-tick (`) syntax. For example:

@Component({
  selector: 'app-root',
  template: `<h1>Welcome to {{title}}!</h1>`
})
export class AppComponent {
  title = 'My Angular App';
}

Here, I have declared an inline template with the content being an h1 tag welcoming the user to my Angular application.

2.2.2. External Templates

In contrast to Inline Templates, External Templates are templates that we declare in a separate HTML file and then link it to the component via a URL. For example:

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {
  title = 'My Angular App';
}

Here, I have linked the app.component.html file to the AppComponent component to use it as an external template.

2.3. Using Expressions in Templates

We can also use expressions in templates to access and display data from the component. These expressions can be variables, functions, or basic operations. These expressions are enclosed in curly braces ({}).

2.4. Best Practices when working with Templates

Here are some best practices when working with Templates:

  • Use external templates instead of inline templates for easier code management and maintenance.
  • Keep templates simple and readable.
  • Avoid using too much complex logic in templates.

3. Data Binding in Angular

3.1. Understanding Data Binding

Data Binding is a way to link data between the model and the view. With data binding, we can ensure that the view always updates automatically according to the model and vice versa.

3.2. Types of Data Binding

3.2.1. Interpolation

Interpolation is a way to access data from the component and display it in the template using curly braces ({}). For example:

@Component({
  selector: 'app-root',
  template: `<h1>Welcome to {{title}}!</h1>`
})
export class AppComponent {
  title = 'My Angular App';
}

Here, I have used interpolation to display the value of the title variable in the template.

3.2.2. Property Binding

Property Binding allows us to bind data from the component to a property of an HTML element. We use square brackets [] to perform property binding. For example:

@Component({
  selector: 'app-root',
  template: `<img [src]="imageUrl">`
})
export class AppComponent {
  imageUrl = 'https://example.com/image.png';
}

Here, I have bound the value of the imageUrl variable to the src property of the img tag.

3.2.3. Event Binding

Event Binding allows us to handle user events (such as mouse clicks, key presses, etc.) by calling functions in the component. We use parentheses () to perform event binding. For example:

@Component({
  selector: 'app-root',
  template: `<button (click)="handleClick()">Click me!</button>`
})
export class AppComponent {
  handleClick() {
    alert('Button clicked!');
  }
}

Here, I have defined an event binding to call the handleClick() function when the user clicks the button.

3.2.4. Two-Way Binding

Two-Way Binding is a combination of Property Binding and Event Binding. It allows us to update data in both directions: from the view to the model and from the model to the view. We use the [(...)] syntax to perform two-way binding. For example:

@Component({
  selector: 'app-root',
  template: `<input [(ngModel)]="name">`
})
export class AppComponent {
  name = '';
}

Here, I have defined a two-way binding to link the value of the ngModel property with the name variable. When the user changes the value of the input, the value of the name variable will also change, and vice versa.

4. RxJs and Data Binding

4.1. RxJs in Angular

In Angular, RxJS is a widely used library that helps us handle asynchronous tasks and events based on reactive programming. RxJS provides tools like Observables, Subjects, Operators, etc. to handle and control data in a more flexible way.

4.2. Relationship between RxJs and Data Binding

Data Binding and RxJs both help us handle and control data in Angular applications. While Data Binding helps us link data between the model and the view, RxJs provides tools that help us handle and control data in a more flexible way.

Example of combining RxJs and Data Binding in Angular:

@Component({
  selector: 'app-root',
  template: `<div *ngIf="data$ | async as data">{{data}}</div>`
})
export class AppComponent {
  data$ = of('Hello, World!');
}

Here, I use the async pipe (a feature of RxJs) to subscribe to the data$ Observable and automatically update the view whenever there is new data.

4.3. Example:

Here's a visual example of using RxJs and Data Binding in Angular. In this example, I will create a service to fetch data from an API, then use pipe to transform the data, and finally bind the data to the template.

my.service.ts

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class MyService {
  constructor(private http: HttpClient) { }

  getData(): Observable<any> {
    return this.http.get('https://example.com/api/data');
  }
}

my.component.ts

import { Component, OnInit } from '@angular/core';
import { MyService } from './my.service';
import { map } from 'rxjs/operators';

@Component({
  selector: 'app-my',
  template: `
    <div *ngIf="data$ | async as data">
      <div *ngFor="let item of data">{{item.name}}</div>
    </div>
  `
})
export class MyComponent implements OnInit {
  data$;

  constructor(private myService: MyService) { }

  ngOnInit() {
    this.data$ = this.myService.getData().pipe(
      map(data => data.filter(item => item.isActive))
    );
  }
}

In the example above, I have created a MyService service to fetch data from an API. Then, in the MyComponent component, I use pipe to filter out active items. Finally, I use the async pipe and the ngFor directive to bind the data to the template.

5. Conclusion

Templates and Data Binding are two core concepts in Angular that help us build user interfaces (UI) and control data in applications. Through this article, we have delved deep into Templates and Data Binding, how to use them, and some best practices when working with them.

I hope that after this article, you have gained a solid understanding of Templates and Data Binding in Angular. Try to practice and apply the knowledge you have learned to real projects to master them even more!

5.1 Recap Questions

1. When should you use Inline Templates and when should you use External Templates? You should use Inline Templates when your template is simple and concise, and when you don't need to reuse this template elsewhere. On the other hand, when the template is complex and has the potential for reuse, you should use External Templates.

2. How do you perform Interpolation in Angular? You perform Interpolation in Angular by using curly braces ({}). For example: {{title}}

3. What are the types of Data Binding in Angular? There are 4 types of Data Binding in Angular: Interpolation, Property Binding, Event Binding, and Two-Way Binding.

4. How do you perform Two-Way Binding in Angular? You perform Two-Way Binding in Angular by using the [(...)] syntax. For example: <input [(ngModel)]="name">

5. How do you combine RxJs with Data Binding in Angular? You can combine RxJs with Data Binding in Angular by using the async pipe. The async pipe helps you subscribe to an Observable and automatically update the view whenever there is new data. For example: <div *ngIf="data$ | async as data">{{data}}</div>


日本語版

1. はじめに

みなさん、こんにちは!今日は、Angularでのテンプレートとデータバインディングについて共有します。これらは、すべてのAngular開発者が知っている必要がある知識です。この記事は、Angularで作業している人だけでなく、Angularに初めて触れる人にとっても非常に興味深く役立つ記事になると思います。それでは、始めましょう!

2. Angularのテンプレート

2.1. テンプレートの理解

Angularにおけるテンプレートは、その名前からもわかるように、Angularアプリケーションのユーザーインターフェース(UI)を定義する場所です。イメージとしては、インターフェースを形作るテンプレートのようなものです。

2.2. テンプレートの種類

2.2.1. インラインテンプレート

インラインテンプレートは、バッククォート(`)構文を使用してコンポーネント内で直接宣言するテンプレートです。例えば:

@Component({
  selector: 'app-root',
  template: `<h1>Welcome to {{title}}!</h1>`
})
export class AppComponent {
  title = 'My Angular App';
}

ここでは、インラインテンプレートを使用して、ユーザーをAngularアプリケーションに歓迎するh1タグの内容を宣言しています。

2.2.2. 外部テンプレート

インラインテンプレートとは対照的に、外部テンプレートは別のHTMLファイルに宣言し、それをURL経由でコンポーネントにリンクするテンプレートです。例えば:

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {
  title = 'My Angular App';
}

ここでは、app.component.htmlファイルをAppComponentコンポーネントにリンクして、外部テンプレートとして使用しています。

2.3. テンプレートでの式の使用

テンプレートでは、変数や関数、基本的な演算など、式を使用してコンポーネントからデータをアクセスして表示することもできます。これらの式は中括弧({})で囲まれます。

2.4. テンプレートの作業時のベストプラクティス

テンプレートを扱う際のベストプラクティスをいくつかご紹介します:

  • コードの管理と保守のために、インラインテンプレートではなく外部テンプレートを使用しましょう。
  • テンプレートをシンプルで読みやすい状態に保ちましょう。
  • テンプレート内で複雑なロジックを過度に使用しないようにしましょう。

3. Angularでのデータバインディング

3.1. データバインディングの理解

データバインディングは、モデルとビューの間のデータをリンクする方法です。データバインディングにより、ビューが常にモデルに応じて自動的に更新されることが保証されます。

3.2. データバインディングの種類

3.2.1. インターポレーション

インターポレーションは、中括弧({})を使用してコンポーネントからデータにアクセスし、テンプレートに表示する方法です。例えば:

@Component({
  selector: 'app-root',
  template: `<h1>Welcome to {{title}}!</h1>`
})
export class AppComponent {
  title = 'My Angular App';
}

ここでは、インターポレーションを使用して、テンプレート内でtitle変数の値を表示しています。

3.2.2. プロパティバインディング

プロパティバインディングを使用すると、コンポーネントからHTML要素のプロパティにデータをバインドすることができます。プロパティバインディングには角括弧([])を使用します。例えば:

@Component({
  selector: 'app-root',
  template: `<img [src]="imageUrl">`
})
export class AppComponent {
  imageUrl = 'https://example.com/image.png';
}

ここでは、imageUrl変数の値をimgタグのsrcプロパティにバインドしています。

3.2.3. イベントバインディング

イベントバインディングを使用すると、ユーザーのイベント(マウスクリック、キープレスなど)をコンポーネント内の関数呼び出しによって処理することができます。イベントバインディングには丸括弧(())を使用します。例えば:

@Component({
  selector: 'app-root',
  template: `<button (click)="handleClick()">Click me!</button>`
})
export class AppComponent {
  handleClick() {
    alert('Button clicked!');
  }
}

ここでは、ボタンをクリックした際にhandleClick()関数を呼び出すイベントバインディングを定義しています。

3.2.4. 両方向バインディング

両方向バインディングは、プロパティバインディングとイベントバインディングを組み合わせたものです。ビューからモデルへのデータの更新と、モデルからビューへのデータの更新の両方を行うことができます。両方向バインディングには[(...)]の構文を使用します。例えば:

@Component({
  selector: 'app-root',
  template: `<input [(ngModel)]="name">`
})
export class AppComponent {
  name = '';
}

ここでは、ngModelプロパティの値をname変数にリンクする両方向バインディングを定義しています。ユーザーが入力の値を変更すると、name変数の値も変更され、その逆も同様です。

4. RxJsとデータバインディング

4.1. AngularでのRxJs

Angularでは、非同期タスクやリアクティブプログラミングに基づくイベントの処理をサポートするために広く使用されているライブラリであるRxJSがあります。RxJSは、Observables、Subjects、Operatorsなどのツールを提供し、データをより柔軟に処理および制御するのに役立ちます。

4.2. RxJsとデータバインディングの関係

データバインディングとRxJsは、どちらもAngularアプリケーションでデータを処理および制御するのに役立ちます。データバインディングはモデルとビューの間のデータをリンクするのに対し、RxJsはより柔軟にデータを処理および制御するためのツールを提供します。

AngularでRxJsとデータバインディングを組み合わせる例:

@Component({
  selector: 'app-root',
  template: `<div *ngIf="data$ | async as data">{{data}}</div>`
})
export class AppComponent {
  data$ = of('Hello, World!');
}

ここでは、asyncパイプ(RxJsの機能)を使用してdata$オブザーバブルにサブスクライブし、新しいデータがあるたびにビューを自動的に更新しています。

4.3. 例:

以下は、AngularでRxJsとデータバインディングを使用するビジュアルな例です。この例では、APIからデータを取得するためのサービスを作成し、そのデータをpipeを使用して変換し、最後にテンプレートにデータをバインドします。

my.service.ts

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class MyService {
  constructor(private http: HttpClient) { }

  getData(): Observable<any> {
    return this.http.get('https://example.com/api/data');
  }
}

my.component.ts

import { Component, OnInit } from '@angular/core';
import { MyService } from './my.service';
import { map } from 'rxjs/operators';

@Component({
  selector: 'app-my',
  template: `
    <div *ngIf="data$ | async as data">
      <div *ngFor="let item of data">{{item.name}}</div>
    </div>
  `
})
export class MyComponent implements OnInit {
  data$;

  constructor(private myService: MyService) { }

  ngOnInit() {
    this.data$ = this.myService.getData().pipe(
      map(data => data.filter(item => item.isActive))
    );
  }
}

上記の例では、MyServiceサービスを作成してAPIからデータを取得しています。そして、MyComponentコンポーネントでは、pipeを使用してアクティブなアイテムをフィルタリングしています。最後に、asyncパイプとngForディレクティブを使用してデータをテンプレートにバインドしています。

5. 結論

テンプレートとデータバインディングは、Angularでのユーザーインターフェース(UI)の構築とデータの制御における2つの基本的な概念です。この記事を通じて、テンプレートとデータバインディングの使用方法や、作業時のベストプラクティスについて詳しく学びました。

この記事を読んだ後、テンプレートとデータバインディングについてしっかりと理解していただけたことを願っています。学んだ知識を実際のプロジェクトに実践してみて、さらに習得してください!

5.1 確認の質問

1. インラインテンプレートと外部テンプレートは、どんな場合に使用するべきですか? インラインテンプレートは、テンプレートがシンプルで簡潔であり、他の場所で再利用する必要がない場合に使用します。一方、テンプレートが複雑で再利用の可能性がある場合は、外部テンプレートを使用するべきです。

2. Angularでのインターポレーションはどのように行いますか? Angularでのインターポレーションは、中括弧({})を使用して行います。例: {{title}}

3. Angularでのデータバインディングの種類は何ですか? Angularでのデータバインディングには、インターポレーション、プロパティバインディング、イベントバインディング、両方向バインディングの4つの種類があります。

4. Angularでの両方向バインディングはどのように行いますか? Angularでの両方向バインディングは、[(...)]の構文を使用します。例: <input [(ngModel)]="name">

5. AngularでRxJsとデータバインディングを組み合わせるにはどうすればよいですか? AngularでRxJsとデータバインディングを組み合わせるには、asyncパイプを使用します。asyncパイプは、Observableにサブスクライブし、新しいデータがあるたびにビューを自動的に更新するのに役立ちます。例: <div *ngIf="data$ | async as data">{{data}}</div>

Mình hy vọng bạn thích bài viết này và học thêm được điều gì đó mới.

Donate mình một ly cafe hoặc 1 cây bút bi để mình có thêm động lực cho ra nhiều bài viết hay và chất lượng hơn trong tương lai nhé. À mà nếu bạn có bất kỳ câu hỏi nào thì đừng ngại comment hoặc liên hệ mình qua: Zalo - 0374226770 hoặc Facebook. Mình xin cảm ơn.

Momo: NGUYỄN ANH TUẤN - 0374226770

TPBank: NGUYỄN ANH TUẤN - 0374226770 (hoặc 01681423001)

image.png


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí