@libs-ui/pipes-format-text-highlight-by-keyword
v0.2.357-11
Published
> Pipe tô màu (highlight) từ khóa trong chuỗi văn bản, hỗ trợ tìm kiếm không dấu tiếng Việt và tùy chỉnh CSS class.
Readme
@libs-ui/pipes-format-text-highlight-by-keyword
Pipe tô màu (highlight) từ khóa trong chuỗi văn bản, hỗ trợ tìm kiếm không dấu tiếng Việt và tùy chỉnh CSS class.
Giới thiệu
LibsUiPipesFormatTextHighlightByKeywordPipe là một Angular Pipe bọc quanh hàm tiện ích highlightByKeyword từ @libs-ui/utils. Pipe nhận vào một chuỗi văn bản và một từ khóa, trả về chuỗi HTML với các đoạn khớp từ khóa được bọc trong thẻ <span> kèm CSS class tô màu. Pipe hỗ trợ tìm kiếm không phân biệt dấu tiếng Việt (gõ "an" có thể match "An", "ăn", "ân", "ản"...) và không phân biệt hoa/thường.
Tính năng
- ✅ Highlight tất cả các lần xuất hiện của từ khóa trong chuỗi
- ✅ Tìm kiếm không dấu — "an" match "An", "ăn", "ân", "ản"... (hỗ trợ tiếng Việt đầy đủ)
- ✅ Không phân biệt hoa/thường (case-insensitive)
- ✅ Tùy chỉnh CSS class cho phần highlight qua tham số
classHightLight - ✅ Tham số
ignoreHighlightđể bật/tắt highlight linh hoạt mà không cần@iftrong template - ✅ Xử lý an toàn khi
valuehoặcsearchlànull/undefined - ✅ Standalone pipe, dùng được ngay không cần NgModule
Khi nào sử dụng
- Highlight keyword trong danh sách kết quả tìm kiếm (search result page)
- Tô màu từ khóa trong dropdown autocomplete khi người dùng gõ
- Highlight từ khóa trong bảng dữ liệu khi lọc/filter
- Hiển thị đoạn văn preview của search engine nội bộ với keyword được tô màu
- Cần tìm kiếm không phân biệt dấu tiếng Việt (gõ "an" → highlight "An", "Ân", "ăn")
Cài đặt
npm install @libs-ui/pipes-format-text-highlight-by-keywordImport
import { LibsUiPipesFormatTextHighlightByKeywordPipe } from '@libs-ui/pipes-format-text-highlight-by-keyword';
@Component({
standalone: true,
imports: [LibsUiPipesFormatTextHighlightByKeywordPipe],
})
export class ExampleComponent {}Ví dụ sử dụng
1. Highlight cơ bản
import { Component, signal } from '@angular/core';
import { LibsUiPipesFormatTextHighlightByKeywordPipe } from '@libs-ui/pipes-format-text-highlight-by-keyword';
@Component({
standalone: true,
imports: [LibsUiPipesFormatTextHighlightByKeywordPipe],
template: `
<!-- ✅ BẮT BUỘC dùng [innerHTML] — pipe trả về HTML string -->
<p [innerHTML]="text | LibsUiPipesFormatTextHighlightByKeywordPipe : keyword"></p>
<!-- ❌ KHÔNG dùng {{ }} — sẽ hiện HTML string thô, không render -->
<!-- <p>{{ text | LibsUiPipesFormatTextHighlightByKeywordPipe : keyword }}</p> -->
`,
})
export class SearchResultComponent {
protected text = signal('Angular is a powerful framework for building web applications');
protected keyword = signal('angular');
}Output HTML được tạo ra:
<span class="bg-[#19344a] text-white">Angular</span> is a powerful framework for building web applications2. Tìm kiếm không dấu tiếng Việt
import { Component, signal } from '@angular/core';
import { LibsUiPipesFormatTextHighlightByKeywordPipe } from '@libs-ui/pipes-format-text-highlight-by-keyword';
@Component({
standalone: true,
imports: [LibsUiPipesFormatTextHighlightByKeywordPipe],
template: `
<!-- Gõ "an" (không dấu) → highlight "An", "ăn", "ân", "ản"... trong text có dấu -->
<p [innerHTML]="name | LibsUiPipesFormatTextHighlightByKeywordPipe : searchTerm"></p>
`,
})
export class VietnameseSearchComponent {
protected name = signal('Nguyễn Văn An là kỹ sư phần mềm tài năng tại Hà Nội');
protected searchTerm = signal('an');
// "an" (không dấu) sẽ match: "An", "ăn" (tài năng), v.v.
}3. Custom CSS class cho phần highlight
import { Component, signal } from '@angular/core';
import { LibsUiPipesFormatTextHighlightByKeywordPipe } from '@libs-ui/pipes-format-text-highlight-by-keyword';
@Component({
standalone: true,
imports: [LibsUiPipesFormatTextHighlightByKeywordPipe],
template: `
<!-- Highlight màu vàng thay vì mặc định dark navy -->
<p
[innerHTML]="text | LibsUiPipesFormatTextHighlightByKeywordPipe
: keyword
: false
: 'bg-yellow-300 text-gray-900 font-bold'">
</p>
`,
})
export class CustomHighlightComponent {
protected text = signal('Tìm kiếm nhanh với highlight màu tùy chỉnh theo thiết kế');
protected keyword = signal('highlight');
}4. Toggle highlight theo trạng thái tìm kiếm
import { Component, signal } from '@angular/core';
import { LibsUiPipesFormatTextHighlightByKeywordPipe } from '@libs-ui/pipes-format-text-highlight-by-keyword';
@Component({
standalone: true,
imports: [LibsUiPipesFormatTextHighlightByKeywordPipe],
template: `
<!-- Chỉ highlight khi isSearching = true — không cần @if trong template -->
<p
[innerHTML]="text | LibsUiPipesFormatTextHighlightByKeywordPipe
: keyword
: !isSearching()">
</p>
<button (click)="handlerToggleSearch($event)">
{{ isSearching() ? 'Tắt highlight' : 'Bật highlight' }}
</button>
`,
})
export class ToggleHighlightComponent {
protected text = signal('Text sẽ được highlight khi đang trong chế độ tìm kiếm');
protected keyword = signal('highlight');
protected isSearching = signal(false);
protected handlerToggleSearch(event: Event): void {
event.stopPropagation();
this.isSearching.update(v => !v);
}
}5. Dùng trong TypeScript (không qua pipe)
Pipe là thin wrapper của hàm highlightByKeyword trong @libs-ui/utils. Khi cần gọi trong TypeScript, import thẳng hàm đó — không cần inject pipe:
import { highlightByKeyword } from '@libs-ui/utils';
// Highlight cơ bản
const result = highlightByKeyword('Nguyễn Văn An', 'an');
// Output: 'Nguyễn Văn <span class="bg-[#19344a] text-white">An</span>'
// Custom class
const customResult = highlightByKeyword(
'Hello World từ Việt Nam',
'hello',
false,
'bg-yellow-300 text-gray-900 font-bold'
);
// Output: '<span class="bg-yellow-300 text-gray-900 font-bold">Hello</span> World từ Việt Nam'
// Tắt highlight
const plain = highlightByKeyword('Text bình thường', 'text', true);
// Output: 'Text bình thường' (giữ nguyên, không bọc span)
// Trong component — dùng để gán vào property hiển thị qua [innerHTML]
@Component({
standalone: true,
template: `<p [innerHTML]="highlightedHtml"></p>`,
})
export class MyComponent {
highlightedHtml = highlightByKeyword('Kết quả tìm kiếm phần mềm', 'phần mềm');
}6. Dùng trực tiếp pipe.transform() (standalone)
import { LibsUiPipesFormatTextHighlightByKeywordPipe } from '@libs-ui/pipes-format-text-highlight-by-keyword';
const pipe = new LibsUiPipesFormatTextHighlightByKeywordPipe();
// Cú pháp: pipe.transform(value, search, ignoreHighlight?, classHightLight?)
const result1 = pipe.transform('Angular framework', 'angular');
// → '<span class="bg-[#19344a] text-white">Angular</span> framework'
const result2 = pipe.transform('Dữ liệu tiếng Việt', 'du lieu', false, 'bg-blue-200');
// → tìm "du lieu" không dấu → match "Dữ liệu" → bọc span với bg-blue-200
const result3 = pipe.transform(undefined, 'keyword');
// → '__' (CHARACTER_DATA_EMPTY — an toàn với undefined, KHÔNG phải chuỗi rỗng)
const result4 = pipe.transform('Text đầy đủ', 'keyword', true);
// → 'Text đầy đủ' (ignoreHighlight = true → trả về nguyên bản)Transform (Pipe)
Cú pháp template
value | LibsUiPipesFormatTextHighlightByKeywordPipe : search : ignoreHighlight? : classHightLight?Bảng tham số
| Tham số | Type | Bắt buộc | Default | Mô tả | Ví dụ |
|---|---|---|---|---|---|
| value | string \| undefined | ✅ | — | Chuỗi văn bản gốc cần tô màu keyword | 'Nguyễn Văn An' |
| search | string \| undefined | ✅ | — | Từ khóa cần highlight trong chuỗi | 'an' |
| ignoreHighlight | boolean \| undefined | ❌ | undefined | Nếu true, bỏ qua highlight và trả về value gốc không đổi | true / !isSearching() |
| classHightLight | string \| undefined | ❌ | 'bg-[#19344a] text-white' | CSS class áp dụng cho <span> bao quanh keyword. Có thể dùng Tailwind hoặc custom CSS class | 'bg-yellow-300 text-gray-900 font-bold' |
Giá trị trả về
Pipe trả về string — là HTML string chứa các thẻ <span> bao quanh keyword:
<!-- Ví dụ output khi search = 'angular' -->
<span class="bg-[#19344a] text-white">Angular</span> is a powerful frameworkBẮT BUỘC bind qua
[innerHTML]để Angular render HTML thay vì hiển thị text thô.
Logic ẩn quan trọng
Tìm kiếm không dấu (deleteUnicode)
Pipe sử dụng hàm deleteUnicode() nội bộ để chuyển cả value và search về dạng không dấu trước khi so sánh vị trí match. Sau đó lấy substring tương ứng trong value gốc (có dấu) để highlight đúng ký tự.
search = 'an'
→ searchConvert = 'an'
→ valueConvert = 'nguyen van an la ky su tai nang'
→ tìm vị trí 'an' trong valueConvert
→ highlight đúng substring có dấu tương ứng trong value gốcKết quả: "Nguyễn Văn An là kỹ sư tài năng" (cả "An" và "ăn" trong "năng" đều được highlight).
Thứ tự xử lý và early return
- Nếu
valuelà falsy → trả về hằng sốCHARACTER_DATA_EMPTY(giá trị thực tế là chuỗi'__', KHÔNG phải chuỗi rỗng'') - Nếu
searchtrống hoặcignoreHighlight = true→ trả vềvaluegốc nguyên vẹn - Nếu không có match nào → trả về
valuegốc nguyên vẹn - Mọi exception được bắt và log — pipe không bao giờ throw lỗi ra ngoài
Lưu ý quan trọng
⚠️ Bắt buộc dùng [innerHTML]: Pipe trả về HTML string chứa <span> tags. Nếu dùng {{ }} trong template, Angular sẽ escape HTML và hiển thị text thô như <span class="...">keyword</span> thay vì render màu.
⚠️ Bảo mật XSS: Chỉ dùng với dữ liệu đã tin cậy (từ API đã sanitize hoặc dữ liệu nội bộ). Nếu value đến từ user input chưa kiểm tra, hãy sanitize trước khi truyền vào pipe. Không truyền nội dung HTML động chưa qua escapeHtml() / xssFilter().
⚠️ Tên tham số classHightLight: Lưu ý cách viết — chỉ có 1 chữ "l" trong "HightLight" (không phải "Highlight"). Đây là tên tham số trong API hiện tại.
⚠️ Nhiều lần xuất hiện: Pipe highlight TẤT CẢ các lần xuất hiện của keyword trong chuỗi, không chỉ lần đầu tiên.
⚠️ Dùng ignoreHighlight thay @if: Thay vì dùng @if để chọn giữa 2 template (có/không highlight), hãy dùng tham số ignoreHighlight — gọn hơn và không tạo/hủy DOM node.
⚠️ value falsy trả về '__', không phải chuỗi rỗng: Khi value là null/undefined/'', pipe trả về literal '__' (hằng số CHARACTER_DATA_EMPTY) thay vì ''. Nếu hiển thị trực tiếp, UI sẽ thấy __ — cần tự xử lý fallback (vd: @if (value)) trước khi truyền vào pipe nếu không muốn hiển thị __.
