@libs-ui/components-inputs-quill2x-preview
v0.2.357-4
Published
> Component hiển thị nội dung Rich Text HTML từ trình soạn thảo Quill2x một cách an toàn, với tính năng Xem thêm / Thu gọn thông minh.
Downloads
2,077
Readme
@libs-ui/components-inputs-quill2x-preview
Component hiển thị nội dung Rich Text HTML từ trình soạn thảo Quill2x một cách an toàn, với tính năng Xem thêm / Thu gọn thông minh.
Giới thiệu
@libs-ui/components-inputs-quill2x-preview là component chuyên dụng để render nội dung HTML được tạo ra bởi trình soạn thảo Quill2x. Component tự động sanitize nội dung qua LibsUiPipesSecurityTrustPipe để ngăn chặn XSS, đồng thời hỗ trợ cơ chế thu gọn / mở rộng thông minh khi nội dung quá dài. Styling được kế thừa từ Quill Snow theme đảm bảo nội dung hiển thị nhất quán với trình soạn thảo.
Tính năng
- ✅ Hiển thị nhất quán — Kế thừa Quill Snow theme styles, đảm bảo nội dung render giống hệt trình soạn thảo
- ✅ Bảo mật XSS — Tự động sanitize HTML qua
LibsUiPipesSecurityTrustPipevớiuseXssFilter: true - ✅ Xem thêm / Thu gọn — Tự động phát hiện nội dung vượt
maxHeight, hiển thị nút điều khiển và hiệu ứng gradient mờ - ✅ Two-way binding —
expandlàmodel()hỗ trợ kiểm soát trạng thái mở rộng từ bên ngoài - ✅ Custom class — Cho phép truyền thêm CSS class vào container qua
containerClass - ✅ Label tùy biến — Nhãn nút Xem thêm / Thu gọn có thể tùy chỉnh hoặc dùng i18n key mặc định
- ✅ Performance —
ChangeDetectionStrategy.OnPush+ Signal API tối ưu hiệu năng render
Khi nào sử dụng
- Khi cần hiển thị nội dung được tạo ra bởi trình soạn thảo Quill2x (rich text, HTML)
- Khi cần render HTML từ API một cách an toàn mà không lo XSS
- Khi muốn giới hạn không gian hiển thị cho bài viết dài trong danh sách hoặc feed
- Khi cần hiển thị preview nội dung email, mô tả sản phẩm, bài viết blog có định dạng phong phú
Cài đặt
npm install @libs-ui/components-inputs-quill2x-previewImport
import { LibsUiComponentsInputsQuill2xPreviewComponent } from '@libs-ui/components-inputs-quill2x-preview';
@Component({
standalone: true,
imports: [LibsUiComponentsInputsQuill2xPreviewComponent],
// ...
})
export class YourComponent {}Ví dụ sử dụng
Ví dụ 1 — Hiển thị cơ bản
// component.ts
import { Component, signal } from '@angular/core';
import { LibsUiComponentsInputsQuill2xPreviewComponent } from '@libs-ui/components-inputs-quill2x-preview';
@Component({
standalone: true,
imports: [LibsUiComponentsInputsQuill2xPreviewComponent],
templateUrl: './article-detail.component.html',
})
export class ArticleDetailComponent {
protected articleContent = signal<string>(
'<h2>Tiêu đề bài viết</h2><p>Đây là nội dung bài viết với <strong>in đậm</strong> và <em>in nghiêng</em>.</p>'
);
}<!-- article-detail.component.html -->
<libs_ui-components-inputs-quill2x-preview
[data]="articleContent()">
</libs_ui-components-inputs-quill2x-preview>Ví dụ 2 — Xem thêm / Thu gọn cho nội dung dài
// component.ts
import { Component, signal } from '@angular/core';
import { LibsUiComponentsInputsQuill2xPreviewComponent } from '@libs-ui/components-inputs-quill2x-preview';
@Component({
standalone: true,
imports: [LibsUiComponentsInputsQuill2xPreviewComponent],
templateUrl: './news-feed.component.html',
})
export class NewsFeedComponent {
protected postContent = signal<string>(`
<h3>Thông báo quan trọng</h3>
<p>Đây là nội dung bài viết rất dài với nhiều đoạn văn...</p>
<ul>
<li>Điểm thứ nhất cần chú ý</li>
<li>Điểm thứ hai cần chú ý</li>
<li>Điểm thứ ba cần chú ý</li>
<li>Điểm thứ tư cần chú ý</li>
<li>Điểm thứ năm cần chú ý</li>
</ul>
<p>Kết luận của bài viết.</p>
`);
protected isExpanded = signal<boolean>(false);
}<!-- news-feed.component.html -->
<libs_ui-components-inputs-quill2x-preview
[data]="postContent()"
[maxHeight]="120"
[hasButtonCollapseExpand]="true"
[labelButtonViewMore]="'Xem toàn bộ bài viết'"
[labelButtonCollapse]="'Thu gọn bài viết'"
[(expand)]="isExpanded()">
</libs_ui-components-inputs-quill2x-preview>Ví dụ 3 — Tùy biến CSS class và kiểm soát expand từ bên ngoài
// component.ts
import { Component, signal } from '@angular/core';
import { LibsUiComponentsInputsQuill2xPreviewComponent } from '@libs-ui/components-inputs-quill2x-preview';
@Component({
standalone: true,
imports: [LibsUiComponentsInputsQuill2xPreviewComponent],
templateUrl: './product-description.component.html',
})
export class ProductDescriptionComponent {
protected description = signal<string>(
'<p style="color: #ee2d41; font-size: 18px;"><strong>Sản phẩm nổi bật</strong></p><p>Mô tả chi tiết sản phẩm với nhiều thông tin hữu ích.</p>'
);
protected isOpen = signal<boolean>(false);
protected handlerToggleExpand(event: Event): void {
event.stopPropagation();
this.isOpen.update((val) => !val);
}
}<!-- product-description.component.html -->
<button (click)="handlerToggleExpand($event)">
{{ isOpen() ? 'Thu gọn' : 'Mở rộng' }}
</button>
<libs_ui-components-inputs-quill2x-preview
[data]="description()"
[containerClass]="'border border-gray-200 rounded p-2'"
[maxHeight]="200"
[hasButtonCollapseExpand]="true"
[(expand)]="isOpen()">
</libs_ui-components-inputs-quill2x-preview>@Input()
| Input | Type | Default | Mô tả | Ví dụ |
|---|---|---|---|---|
| data | string | (Required) | Chuỗi HTML cần hiển thị. Nội dung sẽ được tự động sanitize trước khi render | [data]="articleHtml()" |
| containerClass | string | undefined | Class CSS bổ sung cho thẻ .ql-container bên trong. Dùng để override style hoặc thêm border/padding | [containerClass]="'border rounded p-2'" |
| expand | boolean | undefined | Trạng thái mở rộng / thu gọn. Là model() — hỗ trợ two-way binding [(expand)] | [(expand)]="isExpanded()" |
| hasButtonCollapseExpand | boolean | undefined (falsy) | Bật tính năng giới hạn chiều cao và nút Xem thêm / Thu gọn. Khi true, component đo chiều cao sau render | [hasButtonCollapseExpand]="true" |
| maxHeight | number | 160 | Chiều cao tối đa tính bằng pixel trước khi kích hoạt nút Xem thêm. Nếu truyền undefined hoặc 0, mặc định về 160 | [maxHeight]="200" |
| labelButtonViewMore | string | 'i18n_view_more' | Nhãn hiển thị trên nút Xem thêm. Nếu không truyền, dùng i18n key i18n_view_more | [labelButtonViewMore]="'Xem toàn bộ'" |
| labelButtonCollapse | string | 'i18n_collapse' | Nhãn hiển thị trên nút Thu gọn. Nếu không truyền, dùng i18n key i18n_collapse | [labelButtonCollapse]="'Thu gọn lại'" |
Logic ngầm quan trọng
Tự động phát hiện nội dung dài
Khi hasButtonCollapseExpand là true, trong ngAfterViewInit component dùng timer(250) chờ DOM render xong rồi so sánh offsetHeight của container với maxHeight + 28:
timer(250)
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => {
this.hasShowMore.set(
this.previewRef().nativeElement.offsetHeight > this.maxHeight() + 28
);
});Delay 250ms là cần thiết để Quill styles được áp dụng đầy đủ trước khi đo chiều cao. Nếu nội dung không vượt ngưỡng, nút Xem thêm không xuất hiện dù hasButtonCollapseExpand = true.
Hiệu ứng gradient fade
Khi nội dung bị thu gọn (hasShowMore() && !expand()), một lớp overlay gradient trắng được chèn tuyệt đối ở đáy container tạo hiệu ứng chuyển tiếp mượt mà:
<div class="absolute bottom-[28px] w-full h-[40px] opacity-[0.5]
bg-[linear-gradient(180deg,rgba(255,255,255,0)_0%,rgba(255,255,255,0.95)_51.56%,#fff_100%)]">
</div>CSS fix Quill 2.x list rendering
Component có SCSS override phức tạp để sửa lỗi đánh số tự động bị sai trong Quill 2.x khi xen kẽ giữa danh sách ordered và bullet. Sử dụng counter-reset và counter-increment tùy chỉnh thay vì dựa vào CSS mặc định của Quill.
Lưu ý quan trọng
⚠️ Sanitization tự động: Component dùng LibsUiPipesSecurityTrustPipe với useXssFilter: true để sanitize HTML. Không cần sanitize thủ công trước khi truyền vào [data].
⚠️ Quill CSS dependency: Component yêu cầu Quill Snow theme CSS được load trên trang để styles hiển thị đúng. Nếu không có, nội dung vẫn render nhưng có thể mất một số định dạng.
⚠️ maxHeight transform: Input maxHeight có transform — nếu truyền undefined hoặc 0, giá trị sẽ tự động về 160. Đây là hành vi có chủ đích để tránh container bị thu gọn về 0px.
⚠️ expand là model(): expand dùng model() thay vì input(). Sử dụng [(expand)] cho two-way binding. Nếu chỉ cần one-way, dùng [expand].
⚠️ hasButtonCollapseExpand + ngAfterViewInit: Nút Xem thêm chỉ hiển thị sau khi DOM render xong (sau 250ms). Không nên kiểm tra hasShowMore đồng bộ ngay khi component khởi tạo.
Demo
npx nx serve core-uiTruy cập: http://localhost:4500/components/inputs/quill2x-preview
