@libs-ui/pipes-get-value-of-object 0.2.356-9 → 0.2.357-1
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
|
@@ -1,6 +1,28 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @libs-ui/pipes-get-value-of-object
|
|
2
2
|
|
|
3
|
-
Pipe
|
|
3
|
+
> Pipe truy cập giá trị object theo đường dẫn chuỗi (dot/bracket notation), hỗ trợ nested path an toàn và giá trị mặc định.
|
|
4
|
+
|
|
5
|
+
## Giới thiệu
|
|
6
|
+
|
|
7
|
+
`LibsUiPipesGetValueOfObjectPipe` cho phép truy cập thuộc tính lồng nhau của object trực tiếp trong template Angular thông qua chuỗi path (`"a.b.c"`, `"items[0].name"`). Pipe tích hợp hàm `get` từ `@libs-ui/utils`, tự động xử lý null/undefined ở mọi cấp độ mà không gây lỗi runtime. Phù hợp cho các cấu trúc dữ liệu động hoặc config được truy cập qua path dạng string.
|
|
8
|
+
|
|
9
|
+
## Tính năng
|
|
10
|
+
|
|
11
|
+
- ✅ Truy cập thuộc tính lồng nhau an toàn (không throw khi gặp null/undefined giữa đường)
|
|
12
|
+
- ✅ Hỗ trợ dot notation: `"user.profile.name"`
|
|
13
|
+
- ✅ Hỗ trợ bracket/array notation: `"items[0].id"` hoặc `"items.0.id"`
|
|
14
|
+
- ✅ Hỗ trợ path mảng dạng chuỗi kết hợp: `"data.list[2].label"`
|
|
15
|
+
- ✅ Cung cấp `defaultValue` khi path không tồn tại hoặc giá trị là `undefined`
|
|
16
|
+
- ✅ Hỗ trợ `keepLastValueIfSignal` để lấy signal thô thay vì unwrap giá trị
|
|
17
|
+
- ✅ Standalone pipe — import trực tiếp vào component, không cần module
|
|
18
|
+
|
|
19
|
+
## Khi nào sử dụng
|
|
20
|
+
|
|
21
|
+
- Cần truy cập nested property từ object có cấu trúc phức tạp mà không muốn viết optional chaining dài trong template
|
|
22
|
+
- Path đến thuộc tính được xác định dưới dạng chuỗi động (vd: từ config, từ API response)
|
|
23
|
+
- Cần hiển thị giá trị mặc định khi property không tồn tại trong object
|
|
24
|
+
- Làm gọn template khi render bảng/list từ data schema không cố định
|
|
25
|
+
- Truy cập phần tử trong mảng theo index thông qua path string
|
|
4
26
|
|
|
5
27
|
## Cài đặt
|
|
6
28
|
|
|
@@ -8,9 +30,7 @@ Pipe hỗ trợ truy cập giá trị của object thông qua đường dẫn (p
|
|
|
8
30
|
npm install @libs-ui/pipes-get-value-of-object
|
|
9
31
|
```
|
|
10
32
|
|
|
11
|
-
##
|
|
12
|
-
|
|
13
|
-
Import `LibsUiPipesGetValueOfObjectPipe` vào component của bạn:
|
|
33
|
+
## Import
|
|
14
34
|
|
|
15
35
|
```typescript
|
|
16
36
|
import { LibsUiPipesGetValueOfObjectPipe } from '@libs-ui/pipes-get-value-of-object';
|
|
@@ -20,29 +40,227 @@ import { LibsUiPipesGetValueOfObjectPipe } from '@libs-ui/pipes-get-value-of-obj
|
|
|
20
40
|
imports: [LibsUiPipesGetValueOfObjectPipe],
|
|
21
41
|
// ...
|
|
22
42
|
})
|
|
23
|
-
export class MyComponent {
|
|
43
|
+
export class MyComponent {}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Ví dụ sử dụng
|
|
47
|
+
|
|
48
|
+
### 1. Truy cập thuộc tính đơn giản
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import { Component } from '@angular/core';
|
|
52
|
+
import { LibsUiPipesGetValueOfObjectPipe } from '@libs-ui/pipes-get-value-of-object';
|
|
53
|
+
|
|
54
|
+
@Component({
|
|
55
|
+
standalone: true,
|
|
56
|
+
imports: [LibsUiPipesGetValueOfObjectPipe],
|
|
57
|
+
template: `
|
|
58
|
+
<p>Tên: {{ user | LibsUiPipesGetValueOfObjectPipe:'name' }}</p>
|
|
59
|
+
<p>Email: {{ user | LibsUiPipesGetValueOfObjectPipe:'email':'Chưa có email' }}</p>
|
|
60
|
+
`,
|
|
61
|
+
})
|
|
62
|
+
export class UserCardComponent {
|
|
24
63
|
user = {
|
|
25
|
-
|
|
26
|
-
|
|
64
|
+
name: 'Nguyễn Văn A',
|
|
65
|
+
role: 'admin',
|
|
66
|
+
};
|
|
67
|
+
// Kết quả: "Tên: Nguyễn Văn A"
|
|
68
|
+
// Kết quả: "Email: Chưa có email" ← dùng defaultValue vì email không tồn tại
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 2. Truy cập thuộc tính lồng nhau
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { Component } from '@angular/core';
|
|
76
|
+
import { LibsUiPipesGetValueOfObjectPipe } from '@libs-ui/pipes-get-value-of-object';
|
|
77
|
+
|
|
78
|
+
@Component({
|
|
79
|
+
standalone: true,
|
|
80
|
+
imports: [LibsUiPipesGetValueOfObjectPipe],
|
|
81
|
+
template: `
|
|
82
|
+
<p>Thành phố: {{ profile | LibsUiPipesGetValueOfObjectPipe:'address.city' }}</p>
|
|
83
|
+
<p>Quốc gia: {{ profile | LibsUiPipesGetValueOfObjectPipe:'address.country':'Không rõ' }}</p>
|
|
84
|
+
<p>Zip: {{ profile | LibsUiPipesGetValueOfObjectPipe:'address.zip':'N/A' }}</p>
|
|
85
|
+
`,
|
|
86
|
+
})
|
|
87
|
+
export class ProfileCardComponent {
|
|
88
|
+
profile = {
|
|
89
|
+
name: 'Trần Thị B',
|
|
90
|
+
address: {
|
|
91
|
+
city: 'Hà Nội',
|
|
92
|
+
country: 'Việt Nam',
|
|
93
|
+
// zip không tồn tại
|
|
27
94
|
},
|
|
28
95
|
};
|
|
96
|
+
// Kết quả: "Thành phố: Hà Nội"
|
|
97
|
+
// Kết quả: "Quốc gia: Việt Nam"
|
|
98
|
+
// Kết quả: "Zip: N/A"
|
|
29
99
|
}
|
|
30
100
|
```
|
|
31
101
|
|
|
32
|
-
|
|
102
|
+
### 3. Truy cập phần tử trong mảng
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { Component } from '@angular/core';
|
|
106
|
+
import { LibsUiPipesGetValueOfObjectPipe } from '@libs-ui/pipes-get-value-of-object';
|
|
107
|
+
|
|
108
|
+
@Component({
|
|
109
|
+
standalone: true,
|
|
110
|
+
imports: [LibsUiPipesGetValueOfObjectPipe],
|
|
111
|
+
template: `
|
|
112
|
+
<!-- Truy cập bằng dot notation -->
|
|
113
|
+
<p>Tag đầu: {{ data | LibsUiPipesGetValueOfObjectPipe:'tags.0' }}</p>
|
|
114
|
+
|
|
115
|
+
<!-- Truy cập bằng bracket notation -->
|
|
116
|
+
<p>Tag thứ hai: {{ data | LibsUiPipesGetValueOfObjectPipe:'tags[1]' }}</p>
|
|
117
|
+
|
|
118
|
+
<!-- Kết hợp path phức tạp -->
|
|
119
|
+
<p>Tên sản phẩm đầu: {{ data | LibsUiPipesGetValueOfObjectPipe:'products[0].name':'Không có sản phẩm' }}</p>
|
|
120
|
+
`,
|
|
121
|
+
})
|
|
122
|
+
export class ProductListComponent {
|
|
123
|
+
data = {
|
|
124
|
+
tags: ['Angular', 'TypeScript', 'RxJS'],
|
|
125
|
+
products: [
|
|
126
|
+
{ id: 1, name: 'Laptop Pro', price: 25000000 },
|
|
127
|
+
{ id: 2, name: 'Chuột không dây', price: 350000 },
|
|
128
|
+
],
|
|
129
|
+
};
|
|
130
|
+
// Kết quả: "Tag đầu: Angular"
|
|
131
|
+
// Kết quả: "Tag thứ hai: TypeScript"
|
|
132
|
+
// Kết quả: "Tên sản phẩm đầu: Laptop Pro"
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 4. Dùng trong `@for` để render bảng cấu hình động
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
import { Component } from '@angular/core';
|
|
140
|
+
import { LibsUiPipesGetValueOfObjectPipe } from '@libs-ui/pipes-get-value-of-object';
|
|
141
|
+
|
|
142
|
+
@Component({
|
|
143
|
+
standalone: true,
|
|
144
|
+
imports: [LibsUiPipesGetValueOfObjectPipe],
|
|
145
|
+
template: `
|
|
146
|
+
<table>
|
|
147
|
+
<tr>
|
|
148
|
+
@for (col of columns; track col.key) {
|
|
149
|
+
<th>{{ col.label }}</th>
|
|
150
|
+
}
|
|
151
|
+
</tr>
|
|
152
|
+
@for (row of rows; track row.id) {
|
|
153
|
+
<tr>
|
|
154
|
+
@for (col of columns; track col.key) {
|
|
155
|
+
<td>{{ row | LibsUiPipesGetValueOfObjectPipe:col.key:'—' }}</td>
|
|
156
|
+
}
|
|
157
|
+
</tr>
|
|
158
|
+
}
|
|
159
|
+
</table>
|
|
160
|
+
`,
|
|
161
|
+
})
|
|
162
|
+
export class DynamicTableComponent {
|
|
163
|
+
columns = [
|
|
164
|
+
{ key: 'name', label: 'Họ tên' },
|
|
165
|
+
{ key: 'info.email', label: 'Email' },
|
|
166
|
+
{ key: 'info.phone', label: 'Điện thoại' },
|
|
167
|
+
];
|
|
168
|
+
|
|
169
|
+
rows = [
|
|
170
|
+
{ id: 1, name: 'Lê Văn C', info: { email: 'c.le@example.com' } },
|
|
171
|
+
{ id: 2, name: 'Phạm Thị D', info: { email: 'd.pham@example.com', phone: '0912345678' } },
|
|
172
|
+
];
|
|
173
|
+
// Cột "Điện thoại" của Lê Văn C sẽ hiển thị "—" vì info.phone không tồn tại
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### 5. Sử dụng standalone qua `pipe.transform()` trong TypeScript
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
import { LibsUiPipesGetValueOfObjectPipe } from '@libs-ui/pipes-get-value-of-object';
|
|
181
|
+
|
|
182
|
+
const pipe = new LibsUiPipesGetValueOfObjectPipe();
|
|
183
|
+
|
|
184
|
+
const user = {
|
|
185
|
+
profile: {
|
|
186
|
+
displayName: 'Admin User',
|
|
187
|
+
roles: ['admin', 'editor'],
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// Lấy giá trị nested
|
|
192
|
+
const name = pipe.transform(user, 'profile.displayName');
|
|
193
|
+
// → 'Admin User'
|
|
194
|
+
|
|
195
|
+
// Lấy phần tử mảng
|
|
196
|
+
const firstRole = pipe.transform(user, 'profile.roles.0');
|
|
197
|
+
// → 'admin'
|
|
198
|
+
|
|
199
|
+
// Dùng defaultValue khi path không tồn tại
|
|
200
|
+
const avatar = pipe.transform(user, 'profile.avatar', 'default-avatar.png');
|
|
201
|
+
// → 'default-avatar.png'
|
|
202
|
+
|
|
203
|
+
// Path không tồn tại, không có defaultValue
|
|
204
|
+
const missing = pipe.transform(user, 'profile.age');
|
|
205
|
+
// → undefined
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Transform
|
|
209
|
+
|
|
210
|
+
| Tham số | Type | Bắt buộc | Mô tả | Ví dụ |
|
|
211
|
+
|---|---|---|---|---|
|
|
212
|
+
| `object` | `Record<any, any>` | ✅ | Object nguồn cần truy cập giá trị | `userObj` |
|
|
213
|
+
| `path` | `string` | ✅ | Đường dẫn đến thuộc tính (dot hoặc bracket notation) | `'profile.name'`, `'items[0].id'` |
|
|
214
|
+
| `defaultValue` | `any` | ❌ | Giá trị trả về khi path không tồn tại hoặc giá trị là `undefined` | `'N/A'`, `0`, `[]` |
|
|
215
|
+
| `keepLastValueIfSignal` | `boolean` | ❌ | Khi `true`, trả về signal thô thay vì unwrap giá trị — dùng nội bộ cho Signal workflow | `true` |
|
|
216
|
+
|
|
217
|
+
**Cú pháp template:**
|
|
33
218
|
|
|
34
219
|
```html
|
|
35
|
-
|
|
36
|
-
|
|
220
|
+
{{ object | LibsUiPipesGetValueOfObjectPipe:'path' }}
|
|
221
|
+
{{ object | LibsUiPipesGetValueOfObjectPipe:'path':'defaultValue' }}
|
|
222
|
+
{{ object | LibsUiPipesGetValueOfObjectPipe:'path':defaultValue:keepLastValueIfSignal }}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Cú pháp standalone (TypeScript):**
|
|
37
226
|
|
|
38
|
-
|
|
39
|
-
|
|
227
|
+
```typescript
|
|
228
|
+
const pipe = new LibsUiPipesGetValueOfObjectPipe();
|
|
229
|
+
pipe.transform(object, 'path');
|
|
230
|
+
pipe.transform(object, 'path', 'defaultValue');
|
|
231
|
+
pipe.transform(object, 'path', 'defaultValue', false);
|
|
40
232
|
```
|
|
41
233
|
|
|
42
|
-
##
|
|
234
|
+
## Hành vi với các trường hợp đặc biệt
|
|
235
|
+
|
|
236
|
+
| Input `object` | Input `path` | `defaultValue` | Kết quả |
|
|
237
|
+
|---|---|---|---|
|
|
238
|
+
| `{ a: { b: 1 } }` | `'a.b'` | — | `1` |
|
|
239
|
+
| `{ a: { b: 1 } }` | `'a.c'` | `'fallback'` | `'fallback'` |
|
|
240
|
+
| `{ a: { b: 1 } }` | `'a.c'` | — | `undefined` |
|
|
241
|
+
| `null` | `'any.path'` | `'default'` | `'default'` |
|
|
242
|
+
| `undefined` | `'any.path'` | `0` | `0` |
|
|
243
|
+
| `{ tags: ['a','b'] }` | `'tags.0'` | — | `'a'` |
|
|
244
|
+
| `{ tags: ['a','b'] }` | `'tags[1]'` | — | `'b'` |
|
|
245
|
+
| `{ tags: ['a','b'] }` | `'tags[5]'` | `'missing'` | `'missing'` |
|
|
246
|
+
| `{ x: 1 }` | `''` (chuỗi rỗng) | — | `{ x: 1 }` (trả về chính object) |
|
|
247
|
+
|
|
248
|
+
## Lưu ý quan trọng
|
|
249
|
+
|
|
250
|
+
⚠️ **Pipe name là PascalCase**: Tên pipe trong template là `LibsUiPipesGetValueOfObjectPipe` (không phải `getValueOfObject` hay dạng camelCase). Cần import đúng class này vào `imports[]` của component.
|
|
251
|
+
|
|
252
|
+
⚠️ **Path dạng chuỗi**: Tham số `path` PHẢI là string literal hoặc biến string — không được truyền object/array trực tiếp làm path.
|
|
253
|
+
|
|
254
|
+
⚠️ **Phân biệt `undefined` và `null`**: Pipe trả về `defaultValue` khi giá trị tại path là `undefined`. Nếu giá trị là `null`, pipe vẫn trả về `null` (không áp dụng defaultValue).
|
|
255
|
+
|
|
256
|
+
⚠️ **`keepLastValueIfSignal` chỉ dùng nội bộ**: Tham số thứ tư dành cho các workflow Signal nội bộ của `@libs-ui/utils`. Không dùng trong code ứng dụng thông thường.
|
|
257
|
+
|
|
258
|
+
⚠️ **Không gọi function trong template**: Không dùng `{{ getValueFromObj(user, 'name') }}` — hãy dùng pipe để tránh gọi function mỗi change detection cycle.
|
|
259
|
+
|
|
260
|
+
## Demo
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
npx nx serve core-ui
|
|
264
|
+
```
|
|
43
265
|
|
|
44
|
-
|
|
45
|
-
| ----------------------- | ------------ | ----------- | ------------------------------------------------------------ |
|
|
46
|
-
| `path` | `string` | `undefined` | Đường dẫn đến thuộc tính (ví dụ: `a.b.c` hoặc `items.0.id`). |
|
|
47
|
-
| `defaultValue` | `any` | `undefined` | Giá trị trả về nếu không tìm thấy thuộc tính. |
|
|
48
|
-
| `keepLastValueIfSignal` | `boolean` | `false` | Giữ giá trị cũ khi input signal đang cập nhật. |
|
|
266
|
+
Truy cập: http://localhost:4500/pipes/get-value-of-object
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"libs-ui-pipes-get-value-of-object.mjs","sources":["../../../../../libs-ui/pipes/get-value-of-object/src/get-value-of-object.pipe.ts","../../../../../libs-ui/pipes/get-value-of-object/src/libs-ui-pipes-get-value-of-object.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { Pipe, PipeTransform } from '@angular/core';\nimport { get } from '@libs-ui/utils';\n\n@Pipe({\n name: 'LibsUiPipesGetValueOfObjectPipe',\n standalone: true,\n})\nexport class LibsUiPipesGetValueOfObjectPipe implements PipeTransform {\n transform(object: Record<any, any>, path: string, defaultValue?: any, keepLastValueIfSignal?: boolean) {\n return get(object, path as any, defaultValue, keepLastValueIfSignal);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAAA;MAQa,+BAA+B,CAAA;AAC1C,IAAA,SAAS,CAAC,MAAwB,EAAE,IAAY,EAAE,YAAkB,EAAE,qBAA+B,EAAA;QACnG,OAAO,GAAG,CAAC,MAAM,EAAE,IAAW,EAAE,YAAY,EAAE,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"libs-ui-pipes-get-value-of-object.mjs","sources":["../../../../../libs-ui/pipes/get-value-of-object/src/get-value-of-object.pipe.ts","../../../../../libs-ui/pipes/get-value-of-object/src/libs-ui-pipes-get-value-of-object.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { Pipe, PipeTransform } from '@angular/core';\nimport { get } from '@libs-ui/utils';\n\n@Pipe({\n name: 'LibsUiPipesGetValueOfObjectPipe',\n standalone: true,\n})\nexport class LibsUiPipesGetValueOfObjectPipe implements PipeTransform {\n transform(object: Record<any, any>, path: string, defaultValue?: any, keepLastValueIfSignal?: boolean) {\n return get(object, path as any, defaultValue, keepLastValueIfSignal);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAAA;MAQa,+BAA+B,CAAA;AAC1C,IAAA,SAAS,CAAC,MAAwB,EAAE,IAAY,EAAE,YAAkB,EAAE,qBAA+B,EAAA;QACnG,OAAO,GAAG,CAAC,MAAM,EAAE,IAAW,EAAE,YAAY,EAAE,qBAAqB,CAAC,CAAC;KACtE;wGAHU,+BAA+B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,IAAA,EAAA,CAAA,CAAA;sGAA/B,+BAA+B,EAAA,YAAA,EAAA,IAAA,EAAA,IAAA,EAAA,iCAAA,EAAA,CAAA,CAAA;;4FAA/B,+BAA+B,EAAA,UAAA,EAAA,CAAA;kBAJ3C,IAAI;AAAC,YAAA,IAAA,EAAA,CAAA;AACJ,oBAAA,IAAI,EAAE,iCAAiC;AACvC,oBAAA,UAAU,EAAE,IAAI;AACjB,iBAAA,CAAA;;;ACPD;;AAEG;;;;"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@libs-ui/pipes-get-value-of-object",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.357-1",
|
|
4
4
|
"peerDependencies": {
|
|
5
5
|
"@angular/core": ">=18.0.0",
|
|
6
|
-
"@libs-ui/utils": "0.2.
|
|
6
|
+
"@libs-ui/utils": "0.2.357-1"
|
|
7
7
|
},
|
|
8
8
|
"sideEffects": false,
|
|
9
9
|
"module": "fesm2022/libs-ui-pipes-get-value-of-object.mjs",
|