@libs-ui/components-component-outlet
v0.2.357-7
Published
> ⚠️ **DEPRECATED** — Base component cũ để chuẩn hóa input cho dynamic component trong Table/List. Không sử dụng trong code mới.
Readme
@libs-ui/components-component-outlet
⚠️ DEPRECATED — Base component cũ để chuẩn hóa input cho dynamic component trong Table/List. Không sử dụng trong code mới.
⚠️ Deprecation Notice
LibsUiComponentsComponentOutletComponent đã bị deprecated và không nên sử dụng trong code mới.
Hàm getDataComponentOutlet trong cấu hình Table/List hiện đã hỗ trợ truyền input tùy chỉnh trực tiếp vào component mà không cần extend base component. Pattern mới linh hoạt hơn, type-safe hơn và không phụ thuộc vào base class này.
Giới thiệu
LibsUiComponentsComponentOutletComponent là một Angular standalone base component với input duy nhất [item]: TYPE_OBJECT. Trước đây, các custom component dùng trong Table/List cần extend class này để nhận dữ liệu từ getDataComponentOutlet. Kể từ khi getDataComponentOutlet hỗ trợ truyền input tùy chỉnh theo từng component, base class này không còn cần thiết nữa.
Package cũng export type TYPE_COMPONENT_OUTLET_DATA = WritableSignal<TYPE_OBJECT> dùng cho reactive data binding.
Tính năng
- ✅ Base component với input chuẩn hóa
[item]kiểuTYPE_OBJECT - ✅ Template rỗng — component con tự định nghĩa template
- ✅ Standalone component, không cần NgModule
- ✅ Export
TYPE_COMPONENT_OUTLET_DATAtype alias
Khi nào sử dụng
🚫 Không sử dụng trong code mới. Xem phần Migration Guide bên dưới.
Lib này chỉ còn giá trị khi:
- Duy trì code cũ đang extend
LibsUiComponentsComponentOutletComponentvà chưa có kế hoạch migrate. - Cần tham chiếu type
TYPE_COMPONENT_OUTLET_DATAtừ package này.
Cài đặt
npm install @libs-ui/components-component-outletImport
import { LibsUiComponentsComponentOutletComponent } from '@libs-ui/components-component-outlet';
import { TYPE_COMPONENT_OUTLET_DATA } from '@libs-ui/components-component-outlet';Migration Guide
Cách cũ — Extend base component (❌ Deprecated)
import { Component } from '@angular/core';
import { LibsUiComponentsComponentOutletComponent } from '@libs-ui/components-component-outlet';
// ❌ KHÔNG làm như này trong code mới
@Component({
selector: 'app-product-cell',
standalone: true,
template: `
<div>
<strong>{{ item().name }}</strong>
<span>{{ item().price }}</span>
</div>
`,
})
export class ProductCellComponent extends LibsUiComponentsComponentOutletComponent {
// Kế thừa input 'item: TYPE_OBJECT' từ base component
}// ❌ getDataComponentOutlet trả về item nguyên xi (không type-safe)
configTemplateText: signal({
fieldKey: 'id',
getComponentOutlet: () => of(ProductCellComponent),
getDataComponentOutlet: (item: any) => item,
})Cách mới — Standalone component + getDataComponentOutlet (✅ Recommended)
import { Component, input } from '@angular/core';
import { of } from 'rxjs';
// ✅ Standalone component với input tùy chỉnh, KHÔNG extend base
@Component({
selector: 'app-product-cell',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div>
<strong>{{ product().name }}</strong>
<span>{{ product().price }}</span>
</div>
`,
})
export class ProductCellComponent {
// Input tùy chỉnh — tên và type hoàn toàn tự do
readonly product = input.required<{ name: string; price: number }>();
}// ✅ getDataComponentOutlet map đúng input name và type
configTemplateText: signal({
fieldKey: 'id',
getComponentOutlet: () => of(ProductCellComponent),
getDataComponentOutlet: (item: any) => ({
product: {
name: item.productName,
price: item.salePrice,
},
}),
})Lợi ích của cách mới:
- Input name tùy chỉnh (
productthay vìitem) — đặt tên rõ nghĩa theo domain - Type-safe với interface cụ thể thay vì
TYPE_OBJECT - Không phụ thuộc vào base class
- Dễ test hơn (standalone component thuần)
Ví dụ sử dụng
Ví dụ 1 — Custom status component trong List (Cách mới)
import { Component, input, ChangeDetectionStrategy } from '@angular/core';
import { of } from 'rxjs';
// Bước 1: Tạo standalone component tùy chỉnh
@Component({
selector: 'app-status-cell',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<span [class]="statusClass()">{{ label().text }}</span>
`,
})
export class StatusCellComponent {
readonly label = input.required<{ text: string; color: string }>();
protected statusClass = computed(() => {
const color = this.label().color;
return color === 'green' ? 'text-green-600 font-medium' : 'text-gray-400';
});
}
// Bước 2: Dùng trong List config
@Component({
selector: 'app-order-list',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [LibsUiComponentsListComponent],
template: `
<libs_ui-components-list [config]="listConfig()" />
`,
})
export class OrderListComponent {
protected listConfig = signal({
type: 'text',
configTemplateText: signal({
fieldKey: 'orderId',
getComponentOutlet: () => of(StatusCellComponent),
getDataComponentOutlet: (item: any) => ({
label: {
text: item.statusText,
color: item.statusColor,
},
}),
}),
});
}Ví dụ 2 — Custom action component trong Table (Cách mới)
import { Component, input, output, ChangeDetectionStrategy } from '@angular/core';
import { of } from 'rxjs';
// Bước 1: Tạo component với output event
@Component({
selector: 'app-action-cell',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<button class="text-blue-500 hover:underline" (click)="handlerView($event)">
Xem {{ row().name }}
</button>
`,
})
export class ActionCellComponent {
readonly row = input.required<{ id: string; name: string }>();
readonly outView = output<{ id: string; name: string }>();
protected handlerView(event: Event): void {
event.stopPropagation();
this.outView.emit(this.row());
}
}
// Bước 2: Dùng trong Table config
@Component({
selector: 'app-user-table',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [LibsUiComponentsTableComponent],
template: `
<libs_ui-components-table [config]="tableConfig()" />
`,
})
export class UserTableComponent {
protected tableConfig = signal({
columns: signal([
{
key: 'action',
title: 'Thao tác',
getComponentOutlet: () => of(ActionCellComponent),
getDataComponentOutlet: (item: any) => ({
row: { id: item.userId, name: item.fullName },
}),
},
]),
});
}Ví dụ 3 — Duy trì code cũ đang extend base (Legacy)
Chỉ dùng khi chưa kịp migrate. Đặt task refactor sang cách mới.
import { Component } from '@angular/core';
import { LibsUiComponentsComponentOutletComponent } from '@libs-ui/components-component-outlet';
import { of } from 'rxjs';
// Code cũ — vẫn hoạt động nhưng không nên dùng trong code mới
@Component({
selector: 'app-legacy-cell',
standalone: true,
template: `
<div class="flex items-center gap-2">
<strong>{{ item().name }}</strong>
<span class="text-xs text-gray-500">{{ item().id }}</span>
</div>
`,
})
export class LegacyCellComponent extends LibsUiComponentsComponentOutletComponent {
// Kế thừa: readonly item = input.required<TYPE_OBJECT>()
}
// Sử dụng trong List
listConfig = signal({
configTemplateText: signal({
fieldKey: 'id',
getComponentOutlet: () => of(LegacyCellComponent),
getDataComponentOutlet: (item: any) => item,
}),
});@Input()
| Input | Type | Default | Mô tả | Ví dụ |
|---|---|---|---|---|
| [item] | TYPE_OBJECT | required | Data object truyền vào component. Là required signal input kế thừa từ base. | [item]="rowData()" |
@Output()
Component này không có output. Nếu cần emit event từ dynamic component, khai báo output() trực tiếp trong component tùy chỉnh (không phụ thuộc vào base class).
Types & Interfaces
import { TYPE_COMPONENT_OUTLET_DATA } from '@libs-ui/components-component-outlet';
import { WritableSignal } from '@angular/core';
import { TYPE_OBJECT } from '@libs-ui/interfaces-types';
// TYPE_COMPONENT_OUTLET_DATA là alias cho WritableSignal<TYPE_OBJECT>
// Dùng khi cần typed signal chứa generic object
const data: TYPE_COMPONENT_OUTLET_DATA = signal({ id: 1, name: 'Item A' });| Type | Definition | Mô tả |
|---|---|---|
| TYPE_COMPONENT_OUTLET_DATA | WritableSignal<TYPE_OBJECT> | Signal có thể ghi chứa generic object, dùng cho reactive data binding |
| TYPE_OBJECT | (từ @libs-ui/interfaces-types) | Generic object type, cho phép truyền bất kỳ cấu trúc object nào |
Lưu ý quan trọng
⚠️ DEPRECATED: Component này đã bị deprecated và sẽ bị xóa trong phiên bản tương lai. Không sử dụng trong code mới.
⚠️ Template rỗng: Base component có template: '' — không render bất kỳ UI nào. Chỉ có tác dụng khi được extend bởi component con tự định nghĩa template.
⚠️ Input bắt buộc: [item] là input.required<TYPE_OBJECT>() — phải truyền giá trị khi dùng, nếu không sẽ báo lỗi runtime.
⚠️ Chỉ extend, không dùng trực tiếp: Nếu render <libs_ui-components-component_outlet [item]="data()"> trực tiếp, component sẽ không hiển thị gì cả do template rỗng.
Demo
npx nx serve core-ui