@libs-ui/pipes-get-value-of-object 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
@@ -1,6 +1,28 @@
1
- # Get Value Of Object Pipe
1
+ # @libs-ui/pipes-get-value-of-object
2
2
 
3
- Pipe hỗ trợ truy cập giá trị của object thông qua đường dẫn (path) chuỗi, hỗ trợ lồng nhau cung cấp giá trị mặc định.
3
+ > Pipe truy cập giá trị object theo đường dẫn chuỗi (dot/bracket notation), hỗ trợ nested path an toàn 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
- ## Cách sử dụng
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
- info: {
26
- name: 'John Doe',
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
- Sử dụng trong template:
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
- <!-- Kết quả: John Doe -->
36
- <p>{{ user | LibsUiPipesGetValueOfObjectPipe:'info.name' }}</p>
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
- <!-- Kết quả: 18 (giá trị mặc định nếu không tìm thấy) -->
39
- <p>{{ user | LibsUiPipesGetValueOfObjectPipe:'info.age':18 }}</p>
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
- ## API
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
- | Tham số | Kiểu dữ liệu | Mặc định | Mô tả |
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;IACtE;wGAHW,+BAA+B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,IAAA,EAAA,CAAA;sGAA/B,+BAA+B,EAAA,YAAA,EAAA,IAAA,EAAA,IAAA,EAAA,iCAAA,EAAA,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;;;ACPD;;AAEG;;;;"}
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.356-9",
3
+ "version": "0.2.357-0",
4
4
  "peerDependencies": {
5
5
  "@angular/core": ">=18.0.0",
6
- "@libs-ui/utils": "0.2.356-9"
6
+ "@libs-ui/utils": "0.2.357-0"
7
7
  },
8
8
  "sideEffects": false,
9
9
  "module": "fesm2022/libs-ui-pipes-get-value-of-object.mjs",