+5

Content Projection Trong Angular

Angular cho phép chúng ta tạo ra giao diện của website dựa trên sự kết hợp giữa nhiều component khác nhau và để có thể giúp developer tạo ra các component có độ tái sử dụng(reusability) cao thì angular đã tạo ra một cơ chế giúp chúng ta nhúng template(html) bên ngoài vào các component, cơ chế này được gọi là content projection.

ng-content

Cơ chế content projection được thực hiện thông qua thẻ ng-content do Angular cung cấp. Nếu chúng ta đặt thẻ <ng-content></ng-content> vào file template(html) của component A thì ta có thể truyền bất cứ thẻ html nào hoặc bất cứ tập hợp của các thẻ html nào vào trong component A thì các thẻ html đó sẽ được render ở vị trí mà <ng-content></ng-content> được đặt trong template của component A.

a.component.html

<div class="component-a">
	<div class="component-a__header">
		<h3>header của component a</h3>
	</div>

	<div class="component-a__body">
		<ng-content></ng-content>
	</div>
</div>

Trước khi mình nhúng template khác vào trong component A.

<app-a></app-a>

Kết quả:

Khi mình nhúng các thẻ html vào component A.

<app-a>
	<div>
		content bên ngoài component-a
	</div>
	<div>
		content bên ngoài component-a
	</div>
</app-a>

Kết quả:

Chúng ta cũng có thể nhúng một hoặc nhiều component khác vào component A

<app-a>
	<app-b></app-b>
	<app-c></app-c>
</app-a>

Kết quả:

Sử dụng nhiều thẻ ng-content cho cùng một component

Bạn cũng có thể đặt nhiều thẻ ng-content trong cùng một template của một component, mỗi thẻ ng-content ở một vị trí khác nhau, cho phép developer khác nhúng nhiều template vào các vị trí khác nhau trong component của bạn,

<div class="component-a">
	<div class="component-a__header">
		<h3>header của component a</h3>
	</div>

	<div class="component-a__body">
		<h3>body</h3>
		<ng-content></ng-content>
	</div>

	<div class="component-a__footer">
		<h3>footer</h3>
		<ng-content></ng-content>
	</div>
</div>

Tuy nhiên, do Angular đang không thể phân biệt được phần template nào sẽ render tại vị trí của thẻ ng-content nào nên nó sẽ render template bên ngoài ở vị trí của thẻ ng-content nằm cuối cùng trong template của component A.

<app-a>
	<div class="body-content">
		nội dung phân body
	</div>
	<div class="footer-content">
		nội dung phần footer
	</div>
</app-a>

Kết quả:

selector

Để phân biệt các thẻ ng-content khác nhau, ta có thể truyền selector(giống selector khi bạn biết css cho các thẻ html) vào thuộc tính select của các ng-content khác nhau.

<div class="component-a">
	<div class="component-a__header">
		<h3>header của component a</h3>
	</div>

	<div class="component-a__body">
		<h3>body</h3>
		<!-- tìm thẻ html có class chứa body-content -->
		<ng-content select=".body-content"></ng-content>
	</div>

	<div class="component-a__footer">
		<h3>footer</h3>
		<!-- tìm thẻ html có class chứa footer-content -->
		<ng-content select=".footer-content"></ng-content>
	</div>
</div>

Kết quả:

<div class="component-a">
	<div class="component-a__header">
		<h3>header của component a</h3>
	</div>

	<div class="component-a__body">
		<h3>body</h3>
		<!-- tìm thẻ html có tên là main -->
		<ng-content select="main"></ng-content>
	</div>

	<div class="component-a__footer">
		<h3>footer</h3>
		<!-- tìm thẻ html có tên là footer -->
		<ng-content select="footer"></ng-content>
	</div>
</div>

Nhúng template vào component A:

<app-a>
	<main>
		nội dung phân body
	</main>
	<footer>
		nội dung phần footer
	</footer>
</app-a>

Kết quả:

Có một số điều các bạn cần lưu ý đó là:

  • select không phải là một Input của ng-content vì thế, <ng-content [select]="'main'"></ng-content> sẽ tương tự với <ng-content></ng-content>.
  • select không xử lý được dấu cách vì thế các bạn cũng không thể viết các selector phức tạp như khi các bạn query trong css được.

Lợi ích của việc áp dụng content injection

Việc tạo ra một component cho phép nhúng các template bên ngoài vào template của component đó sẽ giúp giúp chúng ta tái sử dụng tại một template trong nhiều layout khác nhau, đặc biết là những layout có chung một phần khung bao bên ngoài, nhưng nội dung thì lại có nhiều khác biệt.

Tuy ng-content có rất nhiều tác dụng, nhưng nó vẫn có một điểm yếu, đó là chúng ta không thể truy cập vào các property hay các method của các component được truyền vào trong ng-content.

Lời kết

Đôi khi thay vì việc sử dụng if else để render layout cho từng trường hợp khác nhau thì việc sử dụng content inject lại là một lựa chọn tốt hơn, nó sẽ giảm bớt gánh nặng về logic cho các component và tăng tính tái sử dụng cho chúng. Chúc các bạn một ngày làm việc vui vẻ và hiệu quả. Cheer !


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í