diff --git a/Day038-dynamic-component.md b/Day038-dynamic-component.md
index 66ba6c3..e4bc9f8 100644
--- a/Day038-dynamic-component.md
+++ b/Day038-dynamic-component.md
@@ -13,6 +13,9 @@ Hay ở tình huống khác, chúng ta muốn người dùng phải là
Vậy việc load động 1 component khác trong lúc runtime được thực hiện như thế nào?
Điều đó dẫn ta đến bài hôm nay, **Dynamic Component** sẽ là câu trả lời phù hợp để làm việc này.
+Dưới đây là kết quả của project code hôm nay
+
+
## Coding Practice
### Step 1: Khởi tạo project
@@ -99,22 +102,38 @@ export class ExampleContainerComponent implements OnInit {
Flow chính:
1. Tạo 1 ViewChild trong template. Ở đây là thẻ div **#dynamicComponent**. Đây sẽ là nơi chúng ta load những components vào ở runtime.
-2. Connect **#dynamicComponent** thông qua @ViewChild. Chúng ta sẽ có 1 [ViewContainerRef](###ViewContainerRef)
+
+ ```html
+
+ ```
+
+2. Connect **#dynamicComponent** thông qua **@ViewChild**. Chúng ta sẽ có 1 [ViewContainerRef](###ViewContainerRef)
+
+ ```typescript
+ @ViewChild("dynamicComponent", { read: ViewContainerRef, static: true })
+ containerRef: ViewContainerRef;
+ ```
+
3. Inject [CompanyFactoryResolver](###ComponentFactoryResolver) của Angular vào component ExampleContainerComponent.
+
+ ```typescript
+ constructor(private cfr: ComponentFactoryResolver) {}
+ ```
+
4. Dùng Resolver connect với component nào chúng ta muốn load dynamic.
=> Kết quả sẽ trả về 1 [Component Factory](###ComponentFactory)
-```typescript
-const componentFactory = this.cfr.resolveComponentFactory(
- DynamicContentOneComponent
-);
-```
+ ```typescript
+ const componentFactory = this.cfr.resolveComponentFactory(
+ DynamicContentOneComponent
+ );
+ ```
-Dùng **ViewContainerRef** với Component Factory chúng ta vừa tạo ở trên để load **Dynamic Component**.
+5. Dùng **ViewContainerRef** với Component Factory chúng ta vừa tạo ở trên để load **Dynamic Component**.
-```typescript
-const componentRef = this.containerRef.createComponent(componentFactory);
-```
+ ```typescript
+ const componentRef = this.containerRef.createComponent(componentFactory);
+ ```
### Step 4: Add các dynamic components vào entryComponents
@@ -186,11 +205,11 @@ addDynamicCompOne() {
Hiện tại code sử dụng entryComponents đã cũ và với **Angular Ivy**, chúng ta hoàn toàn không cần sử dụng nữa. Ngoài ra chúng ta có thể sử dụng **Angular Ivy** để **lazy load** các components dynamic.
Code sẽ sửa như sau:
-### Step 7.1: Xóa entryComponents setting in app.module.ts
+#### Step 7.1: Xóa entryComponents setting in app.module.ts
Các bạn hãy vào file app.module.ts xóa đi config đã set ở step 4.
-### Step 7.2: Update code ở container component
+#### Step 7.2: Update code ở container component
- Remove 2 cái import components ở đầu file.
- Sửa 2 hàm addDynamicComp
@@ -216,8 +235,9 @@ Code sẽ như sau:
componentRef.instance.data = "INPUT DATA 2";
}
```
+
Vậy là đã xong, các bạn đã thực hiện thành công việc lazy load các dynamic components mà không phải add trực tiếp vào như ở những step đầu.
-**Lưu ý**: Đối với những bạn nào dùng Angular phiên bản cũ thì nhớ update angular để sử dụng tính năng Angular Ivy.
+**Lưu ý**: Đối với những bạn nào dùng Angular phiên bản cũ thì nhớ update angular để sử dụng tính năng Angular Ivy.
## Concepts
@@ -226,7 +246,7 @@ Vậy là đã xong, các bạn đã thực hiện thành công việc
Nó là một cái container từ đó có thể tạo ra Host View (component khi được khởi tạo sẽ tạo ra view tương ứng), và Embedded View (được tạo từ TemplateRef). Với các view được tạo đó sẽ có nơi để gắn vào (container).
Container có thể chứa các container khác (ng-container chẳng hạn) tạo nên cấu trúc cây. Hay hiểu đơn giản thì nó giống như 1 DOM Element, khi đó có thể add thêm các view khác (Component, Template) vào đó.
-
+[TiepPhan](https://www.tiepphan.com/angular-trong-5-phut-dynamic-component-rendering/)
### ComponentFactory
@@ -236,7 +256,7 @@ Container có thể chứa các container khác (ng-container chẳng hạn) t
Đây là 1 class nhận vào các component để load dynamic và tạo ra 1 component factory của component đó. ViewContainerRef sẽ dùng **ComponentFactory** đó để load dynamic các components.
-## Exercies
+## Exercise
### 1. Replace component, not Add
@@ -252,7 +272,7 @@ Cũng như thử emit event từ ViewChild và nhận, xử lý sự ki
Day 38 chúng ta đã học được những concepts liên quan đến Dynamic Component. Đây là 1 tính năng quan trọng có tính ứng dụng cao. Các bạn có thể thực hành nhiều hơn thông qua các bài tập mình đưa cũng như các nguồn tài liệu mình để dưới đây.
-Mục tiêu của ngày 39 sẽ là
+Mục tiêu của ngày 39 sẽ là **Custom Attribute Directive**.
## Code sample
diff --git a/Day039-custom-attribute-directive.md b/Day039-custom-attribute-directive.md
new file mode 100644
index 0000000..1872813
--- /dev/null
+++ b/Day039-custom-attribute-directive.md
@@ -0,0 +1,230 @@
+# Day 39: Custom Attribute Directive trong Angular
+
+## Introduction
+
+Qua các bài trước, các bạn cũng biết những directives trong Angular như ngFor, ngIf, ngStyle.
+Trước khi bước vào bài mới hôm nay, mình xin tổng quát lại về các directives như sau:
+
+Có 3 loại directives trong Angular:
+
+**Components**: Đây là directive phổ biến nhất, cũng thường gặp nhất trong Angular. Directive này giúp chúng ta đóng gói (encapsulated), và tái sử dụng (reusable). Đây là directive với template, view.
+
+**Structural directives**: Đây là directive dùng để thay đổi cấu trúc của views bằng cách thêm hoặc ẩn, xóa bớt phần tử trong DOM. Ví dụ: ngFor, ngIf.
+
+**Attribute directives**: Đây là directive dùng để thay đổi diện mạo (style, appearance) hoặc hành vi (behavior) của các phần tử DOM hay components. Ví dụ: ngStyle, ngClass
+
+Và bài hôm nay của chúng ta sẽ là tạo 1 **Custom Attribute Directive** để chúng ta có thể tái sử dụng nhiều nơi trong ứng dụng.
+
+Dưới đây là kết quả của project code hôm nay
+
+
+## Coding Practice
+
+### Step 1: Khởi tạo project
+
+```sh
+ng new custom-attribute-directive-demo
+```
+
+### Step 2: Tạo các components và directives
+
+```sh
+ng g c example-component
+```
+
+```sh
+ng generate directive custom-directive-demo
+```
+
+Một lưu ý nhỏ ở đây như sau, để sử dụng được directive này chúng ta cần khai báo nó vào module giống như cách chúng ta khai báo component. Câu lệnh generate trên đã auto khai báo directive trên declarations của AppModule cho chúng ta.
+
+Sau đó chúng ta add example-component vào template của app.component.html như sau:
+
+```html
+
+```
+
+### Step 3: Code directive logic
+
+Chúng ta sẽ inject **ElementRef** vào constructor của directive như sau:
+
+```typescript
+constructor(private el: ElementRef) {
+ this.el.nativeElement.innerText = "DEMO TEXT";
+ this.el.nativeElement.style.color = "blue";
+}
+```
+
+Ở đây chúng ta dùng **ElementRef** để tương tác và thay đổi các properties của DOM element. Cụ thể là **innerText** hay **style.color**.
+
+### Step 4: Apply directive
+
+Chúng ta apply attribute directive bằng cách add nó vào như mọi attribute bình thường khác. Tên của attribute này sẽ nằm ở decorator @Directive. (Chúng ta hoàn toàn có thể sửa lại tên chúng ta muốn. Ở đây mình đã sửa từ appCustomDirectiveDemo -> appCustomDirective)
+
+```typescript
+@Directive({
+ selector: "[appCustomDirective]",
+})
+```
+
+Chúng ta vào file **example-component.component.html** sửa lại như sau để apply custom directive chúng ta vừa tạo vào component này.
+
+```html
+
+```
+
+Vậy là chúng ta đã hoàn thành cách tạo và sử dụng 1 custom attribute directive đơn giản.
+
+### Step 5: Using Render2
+
+Ở step 3, chúng ta đã dùng **ElementRef** để tương tác và thay đổi các properties của DOM element. Điều này đã được warning trong official doc của Angular là không nên vì nó sẽ tạo nguy cơ về cho XSS attacks.
+Và ở chính warning đó, Angular cũng giới thiệu đến **Render2** layer. **Render2** cung cấp những apis để dùng tương tác với DOM element 1 cách an toàn. Vậy nên chúng ta sẽ update code sử dụng **Render2** như sau:
+
+```typescript
+export class CustomDirectiveDemoDirective {
+ constructor(private el: ElementRef, private renderer: Renderer2) {
+ this.customContent("DEMO TEXT", "blue");
+ }
+ private customContent(text: string, color: string) {
+ this.renderer.setProperty(this.el.nativeElement, "innerText", text);
+ this.renderer.setStyle(this.el.nativeElement, "color", color);
+ }
+}
+```
+
+### Step 6: Interact with event of Host Element
+
+Khi chúng ta sử dụng 1 **Custom Directive** cho 1 HTML element, thì element đó sẽ được gọi là **Host Element**.( Ở trường hợp của chúng ta Host Element chính thẻ p ở template của example component )
+
+Vậy hiện tại chúng ta muốn xử lý event của Host Element ở directive thì chúng ta sẽ làm thế nào? Ví dụ, khi hover chuột thì đổi màu text. Ở trường hợp nào chúng ta sẽ tương tác thông qua **HostListener**.
+
+Code chúng ta sẽ update như sau để lắng nghe và xử lý 2 event **mouseenter** and **mouseleave** trên Host Element:
+
+```typescript
+export class CustomDirectiveDemoDirective {
+ constructor(private el: ElementRef, private renderer: Renderer2) {
+ this.customContent("DEMO TEXT", "blue");
+ }
+
+ @HostListener("mouseenter") onMouseEnter() {
+ this.customContent("ON MOUSE ENTER", "orange");
+ }
+
+ @HostListener("mouseleave") onMouseLeave() {
+ this.customContent("DEMO TEXT", "blue");
+ }
+
+ private customContent(text: string, color: string) {
+ this.renderer.setProperty(this.el.nativeElement, "innerText", text);
+ this.renderer.setStyle(this.el.nativeElement, "color", color);
+ }
+}
+```
+
+### Step 7: Pass values into the directive
+
+Tiếp theo, chúng ta sẽ truyền xử lý data truyền từ Host Element vào directive. Cụ thể ở case này chúng ta sẽ truyền màu sẽ thay đổi khi hover từ thẻ p vào.
+
+Ở thẻ p, chúng ta sẽ truyền vào như 1 attribute bình thường.
+
+```html
+
+```
+
+Ở directive, chúng ta sẽ dùng **@Input** để nhận và xử lý.
+
+```typescript
+import {
+ Directive,
+ ElementRef,
+ Renderer2,
+ HostListener,
+ Input,
+} from "@angular/core";
+
+@Directive({
+ selector: "[appCustomDirective]",
+})
+export class CustomDirectiveDemoDirective {
+ @Input() hoverColor: string;
+
+ constructor(private el: ElementRef, private renderer: Renderer2) {
+ this.customContent("DEMO TEXT", "blue");
+ }
+
+ @HostListener("mouseenter") onMouseEnter() {
+ this.customContent("ON MOUSE ENTER", this.hoverColor);
+ }
+
+ @HostListener("mouseleave") onMouseLeave() {
+ this.customContent("DEMO TEXT", "blue");
+ }
+
+ private customContent(text: string, color: string) {
+ this.renderer.setProperty(this.el.nativeElement, "innerText", text);
+ this.renderer.setStyle(this.el.nativeElement, "color", color);
+ }
+}
+```
+
+Vậy là đã xong, các bạn đã thực hiện thành công việc tạo và sử dụng 1 **Custom Attribute Directive** trong Angular.
+
+## Concepts
+
+### ElementRef
+
+Đây là 1 class trong **@angular/core** dùng để tương tác với các DOM element trong template. Tuy nhiên chúng ta không nên sử dụng trực tiếp vì vấn đề security.
+
+### Renderer2
+
+Đây là 1 class cung cấp những apis để tương tác với DOM Element 1 cách an toàn. Chúng ta sẽ dùng nó gọi đến ElementRef.
+
+### HostListener
+
+Đây là 1 decorator được định nghĩa cho việc lắng nghe 1 event của DOM. Chúng ta sẽ dùng để viết hàm xử lý khi event đó diễn ra.
+
+Example:
+
+```typescript
+constructor(private el: ElementRef, private renderer: Renderer2) {
+ this.renderer.setProperty(this.el.nativeElement, "innerText", "DEMO");
+}
+```
+
+## Exercise
+
+### 1. @Input and @Input alias
+
+Hiện tại code đang truyền vào thẻ p 2 attribute là **appCustomDirective** và **hoverColor** cho 2 mục đích khác nhau. Hãy sử dụng **property binding** để chỉ cần truyền 1 cái và làm gọn code đi.
+Tham khảo: [**Pass value into directive**](https://angular.io/guide/attribute-directives#pass-values-into-the-directive-with-an-input-data-binding)
+
+### 2. Interact more with Render2
+
+Hãy sử dụng các apis của Render2 để biến đổi nhiều properties và styles hơn.
+
+## Summary
+
+Day 39 chúng ta đã học được những concepts và cách làm liên quan đến cách **Custom attribute directive**.
+
+Mục tiêu của ngày 40 sẽ là **Custom structural directive**.
+
+## Code sample
+
+- https://github.com/januaryofmine/angular-custom-attribute-directive-example
+
+## References
+
+Các bạn có thể đọc thêm ở các bài viết sau
+
+- https://angular.io/guide/attribute-directives#build-a-simple-attribute-directive
+- https://medium.com/@nishu0505/custom-directives-in-angular-5a843f76bb96
+- https://angular.io/api/core/ElementRef
+
+## Author
+
+[Khanh Tiet](https://github.com/januaryofmine)
+
+`#100DaysOfCodeAngular` `#100DaysOfCode` `#AngularVietNam100DoC_Day39`
+
+[day34]: Day034-template-driven-forms-2.md
+[day38]: Day038-dynamic-component.md
diff --git a/README.md b/README.md
index ebe37c1..bed118b 100644
--- a/README.md
+++ b/README.md
@@ -43,6 +43,8 @@ Series độc quyền cung cấp bởi thành viên của Angular Vietnam về c
| [Day 35: Reactive Forms Trong Angular][day35] | |
| [Day 38: Dynamic Component][day38] |
| |
+| [Day 39: Custom Attribute Directive][day39] |
+| |
## Youtube Playlist
@@ -53,6 +55,7 @@ https://www.youtube.com/playlist?list=PLVmX3uPQtp3vXOXUOl8gDIA_43_pmIdFN
- [Tiep Phan][tieppt]
- [Chau Tran][nartc]
- [Trung Vo][trungk18]
+- [Khanh Tiet][khanhtiet]
[day1]: Day001-Installation.md
[day2]: Day002-AngularApp.md
@@ -90,6 +93,7 @@ https://www.youtube.com/playlist?list=PLVmX3uPQtp3vXOXUOl8gDIA_43_pmIdFN
[day34]: Day034-template-driven-forms-2.md
[day35]: Day035-reactive-forms.md
[day38]: Day038-dynamic-component.md
+[day39]: Day039-custom-attribute-directive.md
[day1-video]: https://youtu.be/NS6P1fpU77o
[day2-video]: https://youtu.be/jgFw8tAgKNs
[day3-video]: https://youtu.be/WrMywdbnQfk
@@ -121,3 +125,4 @@ https://www.youtube.com/playlist?list=PLVmX3uPQtp3vXOXUOl8gDIA_43_pmIdFN
[tieppt]: https://github.com/tieppt
[nartc]: https://github.com/nartc
[trungk18]: https://github.com/trungk18
+[khanhtiet]: (https://github.com/januaryofmine)
\ No newline at end of file
diff --git a/assets/day038.gif b/assets/day038.gif
new file mode 100644
index 0000000..7b42421
Binary files /dev/null and b/assets/day038.gif differ
diff --git a/assets/day039.gif b/assets/day039.gif
new file mode 100644
index 0000000..955d9ec
Binary files /dev/null and b/assets/day039.gif differ