@libs-ui/pipes-check-selected-by-key 0.2.356-9 → 0.2.357-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -2,32 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
> Pipe kiểm tra xem một giá trị có tồn tại trong mảng các key đã chọn hay không.
|
|
4
4
|
|
|
5
|
-
**Version:** 0.2.355-14
|
|
6
|
-
|
|
7
5
|
## Giới thiệu
|
|
8
6
|
|
|
9
|
-
`LibsUiPipesCheckSelectedByKeyPipe` là
|
|
7
|
+
`LibsUiPipesCheckSelectedByKeyPipe` là Angular pure pipe dùng để kiểm tra xem một giá trị có thuộc danh sách các key đã được chọn hay không. Pipe hỗ trợ mọi kiểu dữ liệu (string, number, object reference) và xử lý an toàn các trường hợp null/undefined. Đặc biệt, pipe sử dụng tham số `length` như một trigger để ép pure pipe chạy lại khi nội dung mảng bị mutate mà không thay đổi reference.
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
## Tính năng
|
|
12
10
|
|
|
13
|
-
- ✅ Kiểm tra nhanh sự tồn tại của giá trị trong mảng
|
|
14
|
-
- ✅ Hỗ trợ mọi kiểu dữ liệu
|
|
15
|
-
- ✅
|
|
16
|
-
- ✅
|
|
17
|
-
- ✅ Standalone
|
|
11
|
+
- ✅ Kiểm tra nhanh sự tồn tại của giá trị trong mảng bằng strict equality (`===`)
|
|
12
|
+
- ✅ Hỗ trợ mọi kiểu dữ liệu: `string`, `number`, object reference
|
|
13
|
+
- ✅ Xử lý an toàn `null` / `undefined` — luôn trả về `false` thay vì throw lỗi
|
|
14
|
+
- ✅ Tham số `length` là trigger giúp pure pipe re-run khi mảng bị mutate
|
|
15
|
+
- ✅ Standalone — import trực tiếp vào component không cần NgModule
|
|
18
16
|
|
|
19
17
|
## Khi nào sử dụng
|
|
20
18
|
|
|
21
19
|
- Kiểm tra xem một item có được chọn trong danh sách multi-select hay không
|
|
22
|
-
- Hiển thị trạng thái selected/unselected trong UI (checkbox, highlight)
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
|
|
26
|
-
## ⚠️ Important Notes
|
|
27
|
-
|
|
28
|
-
- **🔄 Tham số length - Pure Pipe Trigger**: Đây là pattern quan trọng để đảm bảo pipe chạy lại khi mảng thay đổi. Vì đây là pure pipe, nó chỉ chạy lại khi reference thay đổi. Nếu bạn `push`/`pop` phần tử vào mảng (mutate) mà giữ nguyên reference, pipe sẽ không tự chạy lại. Tham số `length` buộc pipe phải re-run khi số lượng phần tử thay đổi.
|
|
29
|
-
- **Kiểm tra null/undefined**: Pipe trả về `false` nếu `multiKeys` là `null`, `undefined`, không phải array, hoặc array rỗng.
|
|
30
|
-
- **So sánh strict**: Sử dụng `===` để so sánh, không có type coercion.
|
|
20
|
+
- Hiển thị trạng thái selected/unselected trong UI (checkbox, highlight row, active state)
|
|
21
|
+
- Xác định class CSS động dựa trên danh sách key đã chọn
|
|
22
|
+
- Filter hoặc validate dữ liệu dựa trên tập hợp keys đã chọn
|
|
31
23
|
|
|
32
24
|
## Cài đặt
|
|
33
25
|
|
|
@@ -48,146 +40,164 @@ import { LibsUiPipesCheckSelectedByKeyPipe } from '@libs-ui/pipes-check-selected
|
|
|
48
40
|
export class YourComponent {}
|
|
49
41
|
```
|
|
50
42
|
|
|
51
|
-
## Ví dụ
|
|
43
|
+
## Ví dụ sử dụng
|
|
52
44
|
|
|
53
|
-
###
|
|
54
|
-
|
|
55
|
-
```html
|
|
56
|
-
<!-- Kiểm tra xem 'apple' có trong selectedFruits không -->
|
|
57
|
-
<div [class.selected]="'apple' | LibsUiPipesCheckSelectedByKeyPipe : selectedFruits : selectedFruits.length">Apple {{ ('apple' | LibsUiPipesCheckSelectedByKeyPipe : selectedFruits : selectedFruits.length) ? '✓' : '' }}</div>
|
|
58
|
-
```
|
|
45
|
+
### Ví dụ 1 — Multi-select highlight item
|
|
59
46
|
|
|
60
47
|
```typescript
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
48
|
+
// component.ts
|
|
49
|
+
import { Component, signal } from '@angular/core';
|
|
50
|
+
import { LibsUiPipesCheckSelectedByKeyPipe } from '@libs-ui/pipes-check-selected-by-key';
|
|
64
51
|
|
|
65
|
-
|
|
52
|
+
@Component({
|
|
53
|
+
standalone: true,
|
|
54
|
+
imports: [LibsUiPipesCheckSelectedByKeyPipe],
|
|
55
|
+
templateUrl: './fruit-list.component.html',
|
|
56
|
+
})
|
|
57
|
+
export class FruitListComponent {
|
|
58
|
+
protected selectedFruits = signal<string[]>(['apple', 'banana']);
|
|
59
|
+
protected allFruits = ['apple', 'banana', 'orange', 'grape'];
|
|
60
|
+
|
|
61
|
+
protected handlerToggleFruit(event: MouseEvent, fruit: string): void {
|
|
62
|
+
event.stopPropagation();
|
|
63
|
+
const current = [...this.selectedFruits()];
|
|
64
|
+
const index = current.indexOf(fruit);
|
|
65
|
+
if (index >= 0) {
|
|
66
|
+
current.splice(index, 1);
|
|
67
|
+
} else {
|
|
68
|
+
current.push(fruit);
|
|
69
|
+
}
|
|
70
|
+
this.selectedFruits.set(current);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
66
74
|
|
|
67
75
|
```html
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
76
|
+
<!-- fruit-list.component.html -->
|
|
77
|
+
@for (fruit of allFruits; track fruit) {
|
|
78
|
+
<div
|
|
79
|
+
[class.bg-blue-100]="fruit | LibsUiPipesCheckSelectedByKeyPipe : selectedFruits() : selectedFruits().length"
|
|
80
|
+
[class.border-blue-500]="fruit | LibsUiPipesCheckSelectedByKeyPipe : selectedFruits() : selectedFruits().length"
|
|
81
|
+
class="p-3 border rounded cursor-pointer"
|
|
82
|
+
(click)="handlerToggleFruit($event, fruit)">
|
|
83
|
+
{{ fruit }}
|
|
84
|
+
@if (fruit | LibsUiPipesCheckSelectedByKeyPipe : selectedFruits() : selectedFruits().length) {
|
|
85
|
+
<span>✓</span>
|
|
86
|
+
}
|
|
87
|
+
</div>
|
|
88
|
+
}
|
|
71
89
|
```
|
|
72
90
|
|
|
73
|
-
###
|
|
91
|
+
### Ví dụ 2 — Kiểm tra số trong danh sách ID đã chọn
|
|
74
92
|
|
|
75
|
-
```
|
|
76
|
-
|
|
93
|
+
```typescript
|
|
94
|
+
// order-list.component.ts
|
|
95
|
+
import { Component } from '@angular/core';
|
|
96
|
+
import { LibsUiPipesCheckSelectedByKeyPipe } from '@libs-ui/pipes-check-selected-by-key';
|
|
97
|
+
|
|
98
|
+
@Component({
|
|
99
|
+
standalone: true,
|
|
100
|
+
imports: [LibsUiPipesCheckSelectedByKeyPipe],
|
|
101
|
+
templateUrl: './order-list.component.html',
|
|
102
|
+
})
|
|
103
|
+
export class OrderListComponent {
|
|
104
|
+
protected selectedIds = [1, 42, 100];
|
|
105
|
+
protected orders = [
|
|
106
|
+
{ id: 1, name: 'Order #1' },
|
|
107
|
+
{ id: 42, name: 'Order #42' },
|
|
108
|
+
{ id: 99, name: 'Order #99' },
|
|
109
|
+
];
|
|
110
|
+
}
|
|
77
111
|
```
|
|
78
112
|
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
|
|
113
|
+
```html
|
|
114
|
+
<!-- order-list.component.html -->
|
|
115
|
+
@for (order of orders; track order.id) {
|
|
116
|
+
<div [class.active]="order.id | LibsUiPipesCheckSelectedByKeyPipe : selectedIds : selectedIds.length">
|
|
117
|
+
{{ order.name }}
|
|
118
|
+
</div>
|
|
119
|
+
}
|
|
120
|
+
<!-- Order #1 → active (true) -->
|
|
121
|
+
<!-- Order #42 → active (true) -->
|
|
122
|
+
<!-- Order #99 → không active (false) -->
|
|
82
123
|
```
|
|
83
124
|
|
|
84
|
-
###
|
|
125
|
+
### Ví dụ 3 — Kiểm tra với giá trị undefined / mảng rỗng (edge cases)
|
|
85
126
|
|
|
86
127
|
```html
|
|
87
|
-
<!-- Mảng rỗng -->
|
|
128
|
+
<!-- Mảng rỗng → luôn false -->
|
|
88
129
|
{{ 'apple' | LibsUiPipesCheckSelectedByKeyPipe : [] : 0 }}
|
|
89
130
|
<!-- Kết quả: false -->
|
|
90
131
|
|
|
91
|
-
<!-- multiKeys undefined -->
|
|
132
|
+
<!-- multiKeys undefined → luôn false -->
|
|
92
133
|
{{ 'apple' | LibsUiPipesCheckSelectedByKeyPipe : undefined : undefined }}
|
|
93
134
|
<!-- Kết quả: false -->
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
## API
|
|
97
135
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
value | LibsUiPipesCheckSelectedByKeyPipe : multiKeys : length
|
|
136
|
+
<!-- length = 0 dù mảng có phần tử → false (không dùng cách này) -->
|
|
137
|
+
{{ 'apple' | LibsUiPipesCheckSelectedByKeyPipe : ['apple'] : 0 }}
|
|
138
|
+
<!-- Kết quả: false — BUG nếu bạn không truyền đúng length -->
|
|
102
139
|
```
|
|
103
140
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
| Property | Type | Default | Description |
|
|
107
|
-
| ----------- | ------------------------- | ------- | ------------------------------------------------------------------ |
|
|
108
|
-
| `value` | `any` | - | Giá trị cần kiểm tra |
|
|
109
|
-
| `multiKeys` | `Array<any> \| undefined` | - | Mảng các key đã được chọn |
|
|
110
|
-
| `length` | `number \| undefined` | - | **Pure Pipe Trigger** - Truyền `multiKeys.length` để đảm bảo pipe chạy lại khi mảng thay đổi số lượng phần tử |
|
|
111
|
-
|
|
112
|
-
#### Returns
|
|
113
|
-
|
|
114
|
-
`boolean` - `true` nếu `value` tồn tại trong `multiKeys`, ngược lại `false`
|
|
141
|
+
### Ví dụ 4 — Sử dụng standalone (pipe.transform())
|
|
115
142
|
|
|
116
|
-
|
|
143
|
+
```typescript
|
|
144
|
+
import { LibsUiPipesCheckSelectedByKeyPipe } from '@libs-ui/pipes-check-selected-by-key';
|
|
117
145
|
|
|
118
|
-
|
|
146
|
+
const pipe = new LibsUiPipesCheckSelectedByKeyPipe();
|
|
119
147
|
|
|
120
|
-
|
|
148
|
+
pipe.transform('apple', ['apple', 'banana'], 2); // true
|
|
149
|
+
pipe.transform('orange', ['apple', 'banana'], 2); // false
|
|
150
|
+
pipe.transform('apple', [], 0); // false
|
|
151
|
+
pipe.transform('apple', undefined, undefined); // false
|
|
152
|
+
pipe.transform(42, [1, 42, 100], 3); // true
|
|
153
|
+
pipe.transform('42', [42], 1); // false (strict equality)
|
|
154
|
+
```
|
|
121
155
|
|
|
122
|
-
|
|
156
|
+
## Transform
|
|
123
157
|
|
|
124
|
-
|
|
158
|
+
| Tham số | Type | Bắt buộc | Mô tả | Ví dụ |
|
|
159
|
+
|---|---|---|---|---|
|
|
160
|
+
| `value` | `any` | Có | Giá trị cần kiểm tra sự tồn tại trong mảng | `'apple'`, `42`, `myObj` |
|
|
161
|
+
| `multiKeys` | `Array<any> \| undefined` | Có | Mảng các key đã được chọn | `['apple', 'banana']`, `[1, 42]` |
|
|
162
|
+
| `length` | `number \| undefined` | Có | **Pure Pipe Trigger** — BẮT BUỘC truyền `multiKeys.length` để pipe re-run khi mảng bị mutate | `selectedFruits.length` |
|
|
125
163
|
|
|
126
|
-
|
|
164
|
+
**Trả về:** `boolean` — `true` nếu `value` tồn tại trong `multiKeys`, ngược lại `false`.
|
|
127
165
|
|
|
128
|
-
|
|
129
|
-
// ✅ Đúng: Truyền multiKeys.length
|
|
130
|
-
'apple' | LibsUiPipesCheckSelectedByKeyPipe : selectedFruits : selectedFruits.length
|
|
166
|
+
**Cú pháp template:**
|
|
131
167
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
168
|
+
```html
|
|
169
|
+
{{ value | LibsUiPipesCheckSelectedByKeyPipe : multiKeys : length }}
|
|
170
|
+
[class.selected]="value | LibsUiPipesCheckSelectedByKeyPipe : multiKeys : length"
|
|
135
171
|
```
|
|
136
172
|
|
|
137
|
-
|
|
173
|
+
## Lưu ý quan trọng
|
|
138
174
|
|
|
139
|
-
|
|
140
|
-
// 1. Khi mutate array (push/pop), reference không đổi
|
|
141
|
-
this.selectedKeys.push('new-key'); // Cùng reference
|
|
175
|
+
⚠️ **Tham số `length` là Pure Pipe Trigger — BẮT BUỘC truyền đúng**: Vì đây là pure pipe, Angular chỉ chạy lại khi input thay đổi reference. Khi bạn mutate mảng bằng `push()` / `pop()` / `splice()` mà giữ nguyên reference, pipe sẽ không tự chạy lại. Tham số `length` được truyền vào để ép Angular detect change và buộc pipe re-run khi số lượng phần tử thay đổi. Luôn truyền `myArray.length` — không được hardcode số cố định như `: 0` hay `: 1`.
|
|
142
176
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
177
|
+
```html
|
|
178
|
+
<!-- ✅ ĐÚNG — pipe sẽ re-run khi selectedFruits thay đổi -->
|
|
179
|
+
{{ 'apple' | LibsUiPipesCheckSelectedByKeyPipe : selectedFruits : selectedFruits.length }}
|
|
146
180
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
return false; // ❌ Return false nếu length không khớp
|
|
150
|
-
}
|
|
181
|
+
<!-- ❌ SAI — pipe KHÔNG re-run vì length cố định -->
|
|
182
|
+
{{ 'apple' | LibsUiPipesCheckSelectedByKeyPipe : selectedFruits : 0 }}
|
|
151
183
|
```
|
|
152
184
|
|
|
153
|
-
|
|
185
|
+
⚠️ **So sánh strict equality (`===`)**: Pipe dùng `===` để so sánh, không có type coercion. `'42'` và `42` là hai giá trị khác nhau. Object được so sánh theo reference, không theo value.
|
|
154
186
|
|
|
155
|
-
|
|
187
|
+
```html
|
|
188
|
+
<!-- String '42' ≠ Number 42 → false -->
|
|
189
|
+
{{ '42' | LibsUiPipesCheckSelectedByKeyPipe : [42] : 1 }}
|
|
156
190
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
// 1. multiKeys là null hoặc undefined
|
|
160
|
-
// 2. multiKeys không phải là Array
|
|
161
|
-
// 3. multiKeys.length === 0
|
|
162
|
-
// 4. length parameter là falsy (0, null, undefined)
|
|
163
|
-
|
|
164
|
-
// Edge case:
|
|
165
|
-
// Nếu length = 0 nhưng multiKeys có phần tử
|
|
166
|
-
// Pipe vẫn trả về false (do check !length)
|
|
167
|
-
const result = 'apple' | pipe : ['apple'] : 0;
|
|
168
|
-
// => false (mặc dù 'apple' có trong array)
|
|
191
|
+
<!-- Cùng reference object → true -->
|
|
192
|
+
<!-- Khác reference dù cùng nội dung → false -->
|
|
169
193
|
```
|
|
170
194
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
Pipe sử dụng strict equality (`===`) để so sánh, không có type coercion:
|
|
174
|
-
|
|
175
|
-
```typescript
|
|
176
|
-
// String vs Number
|
|
177
|
-
'42' | pipe : [42] : 1 // => false
|
|
178
|
-
|
|
179
|
-
// Object reference
|
|
180
|
-
const obj = { id: 1 };
|
|
181
|
-
obj | pipe : [obj] : 1 // => true
|
|
182
|
-
obj | pipe : [{ id: 1 }] : 1 // => false (khác reference)
|
|
183
|
-
```
|
|
195
|
+
⚠️ **Pipe trả về `false` cho mọi input không hợp lệ**: `multiKeys` là `null`, `undefined`, không phải array, hoặc array rỗng đều trả về `false`. Tham số `length` là falsy (0, null, undefined) cũng trả về `false` — kể cả khi `multiKeys` có phần tử.
|
|
184
196
|
|
|
185
197
|
## Demo
|
|
186
198
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
## License
|
|
199
|
+
```bash
|
|
200
|
+
npx nx serve core-ui
|
|
201
|
+
```
|
|
192
202
|
|
|
193
|
-
|
|
203
|
+
Truy cập: http://localhost:4500/pipes/check-selected-by-key
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"libs-ui-pipes-check-selected-by-key.mjs","sources":["../../../../../libs-ui/pipes/check-selected-by-key/src/check-selected-by-key.pipe.ts","../../../../../libs-ui/pipes/check-selected-by-key/src/libs-ui-pipes-check-selected-by-key.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { Pipe, PipeTransform } from '@angular/core';\n@Pipe({\n name: 'LibsUiPipesCheckSelectedByKeyPipe',\n standalone: true,\n})\nexport class LibsUiPipesCheckSelectedByKeyPipe implements PipeTransform {\n transform(value: any, multiKeys: Array<any> | undefined, length: number | undefined): boolean {\n if (!multiKeys || !(multiKeys instanceof Array) || !multiKeys.length || !length) {\n return false;\n }\n\n return multiKeys.some((key) => key === value);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;AAAA;MAMa,iCAAiC,CAAA;AAC5C,IAAA,SAAS,CAAC,KAAU,EAAE,SAAiC,EAAE,MAA0B,EAAA;AACjF,QAAA,IAAI,CAAC,SAAS,IAAI,EAAE,SAAS,YAAY,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE;AAC/E,YAAA,OAAO,KAAK;
|
|
1
|
+
{"version":3,"file":"libs-ui-pipes-check-selected-by-key.mjs","sources":["../../../../../libs-ui/pipes/check-selected-by-key/src/check-selected-by-key.pipe.ts","../../../../../libs-ui/pipes/check-selected-by-key/src/libs-ui-pipes-check-selected-by-key.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { Pipe, PipeTransform } from '@angular/core';\n@Pipe({\n name: 'LibsUiPipesCheckSelectedByKeyPipe',\n standalone: true,\n})\nexport class LibsUiPipesCheckSelectedByKeyPipe implements PipeTransform {\n transform(value: any, multiKeys: Array<any> | undefined, length: number | undefined): boolean {\n if (!multiKeys || !(multiKeys instanceof Array) || !multiKeys.length || !length) {\n return false;\n }\n\n return multiKeys.some((key) => key === value);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;AAAA;MAMa,iCAAiC,CAAA;AAC5C,IAAA,SAAS,CAAC,KAAU,EAAE,SAAiC,EAAE,MAA0B,EAAA;AACjF,QAAA,IAAI,CAAC,SAAS,IAAI,EAAE,SAAS,YAAY,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE;AAC/E,YAAA,OAAO,KAAK,CAAC;SACd;AAED,QAAA,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,KAAK,KAAK,CAAC,CAAC;KAC/C;wGAPU,iCAAiC,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,IAAA,EAAA,CAAA,CAAA;sGAAjC,iCAAiC,EAAA,YAAA,EAAA,IAAA,EAAA,IAAA,EAAA,mCAAA,EAAA,CAAA,CAAA;;4FAAjC,iCAAiC,EAAA,UAAA,EAAA,CAAA;kBAJ7C,IAAI;AAAC,YAAA,IAAA,EAAA,CAAA;AACJ,oBAAA,IAAI,EAAE,mCAAmC;AACzC,oBAAA,UAAU,EAAE,IAAI;AACjB,iBAAA,CAAA;;;ACLD;;AAEG;;;;"}
|