@libs-ui/services-dynamic-component
v0.2.355-15
Published
> Service để tạo và quản lý Angular components động tại runtime.
Readme
@libs-ui/services-dynamic-component
Service để tạo và quản lý Angular components động tại runtime.
📦 Cài đặt
npm install @libs-ui/services-dynamic-component🚀 Sử dụng
import { LibsUiDynamicComponentService } from '@libs-ui/services-dynamic-component';
@Component({...})
export class MyComponent {
private dynamicService = inject(LibsUiDynamicComponentService);
createComponent() {
const componentRef = this.dynamicService.resolveComponentFactory(MyDynamicComponent);
this.dynamicService.addToBody(componentRef);
}
}📚 API Reference
Methods
| Method | Parameters | Returns | Description |
|--------|-----------|---------|-------------|
| resolveComponentFactory | component: any | ComponentRef<any> | Tạo ComponentRef từ component class |
| addToBody | componentRef?: ComponentRef<any>, isAddParentDocument?: boolean, timerDelayUpdateStyleToParent?: number | string | Thêm component vào document.body hoặc parent document |
| addToElement | componentRef: any, elementAdd: any | void | Thêm component vào element cụ thể |
| addToElementLayoutContentDefault | componentRef: any, id?: string | void | Thêm component vào element có id mặc định |
| addToIdAttributeElement | componentRef: any, id: string | void | Thêm component vào element có id cụ thể |
| delete | componentRef: any, options?: { ignoreDestroyComponent?: boolean; timeoutDestroy?: number } | void | Xóa component khỏi DOM và destroy |
| remove | componentRef: any, _?: string, ignoreDestroyComponent?: boolean | void | ⚠️ Deprecated - Dùng delete() thay thế |
Properties
| Property | Type | Description |
|----------|------|-------------|
| IdElementLayoutContentDefault | string (setter) | Set ID mặc định cho element layout content |
🔍 Hidden Logic (Logic Ẩn)
1. Singleton Service & State Sharing
Service là singleton (providedIn: 'root'), nghĩa là:
- Tất cả instances trong app share cùng một state
elementLayoutContentMap được cache và share giữa tất cả usageselementBodyđược cache một lần và reuse
Ví dụ:
// Component A
this.dynamicService.addToIdAttributeElement(compRef, 'container-1');
// Component B - Cùng service instance
// elementLayoutContent Map đã có 'container-1' được cache
this.dynamicService.addToIdAttributeElement(compRef2, 'container-1');
// Không cần query DOM lại2. Element Caching với Map
Service sử dụng Map<string, HTMLElement> để cache DOM elements:
- Lần đầu gọi
addToIdAttributeElement()với id mới: Query DOM và cache - Lần sau: Lấy từ cache, không query DOM lại
- Lưu ý: Nếu element bị xóa khỏi DOM, cache vẫn giữ reference cũ
Ví dụ:
// Lần 1: Query DOM và cache
this.dynamicService.addToIdAttributeElement(compRef1, 'my-container');
// elementLayoutContent.set('my-container', document.getElementById('my-container'))
// Lần 2: Lấy từ cache (nhanh hơn)
this.dynamicService.addToIdAttributeElement(compRef2, 'my-container');
// elementLayoutContent.get('my-container') - không query DOM3. Parent Document Handling (iframe scenarios)
Khi isAddParentDocument = true:
- Component được append vào
window.parent.document.body(không phải current document) - Styles từ current document được clone và append vào parent document
- WeakMap được dùng để track styles đã copy, tránh duplicate
Ví dụ:
// Trong iframe
const compRef = this.dynamicService.resolveComponentFactory(MyComponent);
this.dynamicService.addToBody(compRef, true);
// Component xuất hiện trong parent window, không phải iframe
// Styles từ iframe được copy sang parent document4. Style Copying Logic
Khi add component vào parent document:
- Tất cả
<style>tags từ current document được clone - Cloned styles được append vào parent document head
- WeakMap track ComponentRef → Array để cleanup sau
- Có debounce delay (mặc định 250ms) để tránh copy nhiều lần
Ví dụ:
// Current document có 3 style tags
// Khi addToBody(compRef, true):
// - Clone 3 style tags
// - Append vào parent.document.head
// - Track trong mapStylesToParentDocument5. ComponentRef Lifecycle Management
Service quản lý lifecycle của ComponentRef:
attachView(): Đăng ký view với ApplicationRef (change detection)detachView(): Hủy đăng ký view (ngừng change detection)destroy(): Destroy component hoàn toàn
Ví dụ:
const compRef = this.dynamicService.resolveComponentFactory(MyComponent);
this.dynamicService.addToBody(compRef);
// attachView() được gọi tự động
// Sau đó
this.dynamicService.delete(compRef);
// detachView() được gọi, sau đó destroy()6. Delete Options - ignoreDestroyComponent & timeoutDestroy
delete() method có 2 options quan trọng:
ignoreDestroyComponent = true:
- Chỉ detach view, không destroy component
- Component vẫn tồn tại trong memory
- Dùng khi muốn reuse component sau
timeoutDestroy:
- Delay destroy component sau khi detach view
- Dùng khi cần animation hoặc cleanup async
Ví dụ:
// Option 1: Không destroy (reuse sau)
this.dynamicService.delete(compRef, { ignoreDestroyComponent: true });
// Component vẫn tồn tại, có thể add lại
// Option 2: Delay destroy (animation)
this.dynamicService.delete(compRef, { timeoutDestroy: 300 });
// detachView() ngay, destroy() sau 300ms7. Auto Cleanup trong ngOnDestroy
Service tự động cleanup khi bị destroy:
- Clear timeout nếu có
- Clear elementLayoutContent Map
- Lưu ý: Styles đã copy sang parent document KHÔNG được tự động remove
Ví dụ:
// Service được destroy
ngOnDestroy() {
// Clear timeout
// Clear elementLayoutContent Map
// Nhưng styles trong parent document vẫn còn
}8. WeakMap cho Style Tracking
Service dùng WeakMap để track styles:
- Key: ComponentRef (weak reference, không giữ component alive)
- Value: Array (cloned style tags)
- Khi ComponentRef bị GC, entry tự động bị xóa khỏi WeakMap
Ví dụ:
const compRef = this.dynamicService.resolveComponentFactory(MyComponent);
this.dynamicService.addToBody(compRef, true);
// mapStylesToParentDocument.set(compRef, [style1, style2, style3])
// Sau khi delete
this.dynamicService.delete(compRef);
// removeStyleToParentDocument() xóa styles khỏi parent document
// WeakMap entry vẫn tồn tại cho đến khi compRef bị GC⚠️ Important Notes
- Service là singleton, state được share giữa tất cả usages
- Element caching có thể trở nên stale nếu element bị xóa khỏi DOM
- Styles copy sang parent document cần được cleanup thủ công nếu service bị destroy
remove()method đã deprecated, dùngdelete()thay thế- ComponentRef cần được destroy để tránh memory leaks
📖 Ví dụ
Xem demo tại: http://localhost:4500/services/dynamic-component
📝 Version
0.2.355-10
