@libs-ui/pipes-clone-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 +165 -185
- package/fesm2022/libs-ui-pipes-clone-object.mjs.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,90 +1,26 @@
|
|
|
1
1
|
# @libs-ui/pipes-clone-object
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
>
|
|
5
|
-
> Pipe clone object/array với hỗ trợ shallow clone, deep clone và xử lý Signal tự động.
|
|
3
|
+
> Angular pipe clone object/array với hỗ trợ shallow clone, deep clone và tự động unwrap Signal.
|
|
6
4
|
|
|
7
5
|
## Giới thiệu
|
|
8
6
|
|
|
9
|
-
`LibsUiPipesCloneObjectPipe` là một Angular pipe đa năng để clone object hoặc array. Pipe hỗ trợ cả shallow clone (nhanh, ít memory) và deep clone (an toàn cho nested objects), đồng
|
|
7
|
+
`LibsUiPipesCloneObjectPipe` là một Angular standalone pipe đa năng dùng để clone object hoặc array trực tiếp trong template. Pipe hỗ trợ cả shallow clone (nhanh, ít memory) và deep clone (an toàn cho nested objects), đồng thời tự động phát hiện và unwrap Angular Signal nếu input là Signal — không cần gọi `()` trong template. Khi input là falsy, pipe trả về `defaultData` hoặc object rỗng `{}` để tránh lỗi `undefined`.
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
## Tính năng
|
|
12
10
|
|
|
13
|
-
- ✅ Shallow clone với spread operator (nhanh
|
|
14
|
-
- ✅ Deep clone với
|
|
15
|
-
- ✅
|
|
16
|
-
- ✅ Default data khi input falsy
|
|
11
|
+
- ✅ Shallow clone với spread operator (`{ ...obj }` / `[ ...arr ]`) — nhanh, ít memory
|
|
12
|
+
- ✅ Deep clone với `cloneDeep` từ `@libs-ui/utils` — an toàn cho nested objects
|
|
13
|
+
- ✅ Tự động unwrap Signal — không cần gọi `.()` trong template
|
|
14
|
+
- ✅ Default data khi input là falsy (`null`, `undefined`, `0`, `""`, `false`)
|
|
17
15
|
- ✅ Hoạt động với cả object và array
|
|
18
|
-
- ✅ Standalone pipe (Angular 16+)
|
|
16
|
+
- ✅ Standalone pipe (Angular 16+), không cần NgModule
|
|
19
17
|
|
|
20
18
|
## Khi nào sử dụng
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
```typescript
|
|
27
|
-
// ❌ Sai: Gán trực tiếp - cùng reference
|
|
28
|
-
this.editUser = this.user;
|
|
29
|
-
this.editUser.name = 'Jane'; // ❌ user.name cũng đổi!
|
|
30
|
-
|
|
31
|
-
// ✅ Đúng: Clone trước khi edit
|
|
32
|
-
this.editUser = cloneDeep(this.user);
|
|
33
|
-
this.editUser.name = 'Jane'; // ✅ user.name không đổi
|
|
34
|
-
// Khi Cancel: đơn giản là bỏ editUser
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
### 🔽 Pass Data xuống Child Component
|
|
38
|
-
|
|
39
|
-
Khi truyền data xuống component con nhưng không muốn component con làm thay đổi data cha:
|
|
40
|
-
|
|
41
|
-
```typescript
|
|
42
|
-
// Parent Component
|
|
43
|
-
items = ['apple', 'banana'];
|
|
44
|
-
|
|
45
|
-
// ❌ Child có thể mutate parent data
|
|
46
|
-
<app-child [items]="items">
|
|
47
|
-
// Child: items.push('orange') => Parent.items cũng đổi!
|
|
48
|
-
|
|
49
|
-
// ✅ Clone trước khi truyền
|
|
50
|
-
<app-child [items]="items | LibsUiPipesCloneObjectPipe">
|
|
51
|
-
// Child tự do modify mà không ảnh hưởng Parent
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### ⏪ Undo/Redo Functionality
|
|
55
|
-
|
|
56
|
-
Lưu trữ các state cũ để khôi phục:
|
|
57
|
-
|
|
58
|
-
```typescript
|
|
59
|
-
history = [cloneDeep(currentState)];
|
|
60
|
-
|
|
61
|
-
// Sau khi thay đổi
|
|
62
|
-
history.push(cloneDeep(newState));
|
|
63
|
-
|
|
64
|
-
// Undo: khôi phục state trước đó
|
|
65
|
-
currentState = cloneDeep(history[history.length - 2]);
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
### 🔄 Signal Unwrap & Clone
|
|
69
|
-
|
|
70
|
-
Đơn giản hóa template khi làm việc với Signal:
|
|
71
|
-
|
|
72
|
-
```typescript
|
|
73
|
-
// ❌ Không dùng pipe - phải gọi .() nhiều lần
|
|
74
|
-
<p>{{ userSignal().name }}</p>
|
|
75
|
-
<p>{{ userSignal().email }}</p>
|
|
76
|
-
|
|
77
|
-
// ✅ Dùng pipe - auto unwrap + clone
|
|
78
|
-
@let user = (userSignal | LibsUiPipesCloneObjectPipe);
|
|
79
|
-
<p>{{ user.name }}</p>
|
|
80
|
-
<p>{{ user.email }}</p>
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
## ⚠️ Important Notes
|
|
84
|
-
|
|
85
|
-
- **🔄 Signal Auto-Unwrap**: Pipe tự động unwrap Signal nếu input là Signal. Không cần gọi `.()` trong template.
|
|
86
|
-
- **🔀 Shallow vs Deep**: Mặc định shallow clone (spread). Dùng `isCloneDeep = true` để deep clone nested objects.
|
|
87
|
-
- **📦 Default Data**: Nếu input falsy, trả về `defaultData` hoặc empty object `{}`.
|
|
20
|
+
- **Form Editing với Cancel**: Clone data trước khi cho user chỉnh sửa. Khi Cancel, data gốc không bị ảnh hưởng vì form đang thao tác trên bản clone.
|
|
21
|
+
- **Truyền data xuống Child Component**: Clone trước khi truyền để component con tự do modify mà không làm thay đổi data ở component cha.
|
|
22
|
+
- **Undo/Redo Functionality**: Lưu trữ các snapshot state cũ để có thể khôi phục.
|
|
23
|
+
- **Signal Unwrap trong template**: Dùng pipe để tự động unwrap Signal và clone giá trị, giúp template gọn hơn khi cần truy cập nhiều property.
|
|
88
24
|
|
|
89
25
|
## Cài đặt
|
|
90
26
|
|
|
@@ -105,170 +41,214 @@ import { LibsUiPipesCloneObjectPipe } from '@libs-ui/pipes-clone-object';
|
|
|
105
41
|
export class YourComponent {}
|
|
106
42
|
```
|
|
107
43
|
|
|
108
|
-
##
|
|
44
|
+
## Transform
|
|
109
45
|
|
|
110
|
-
### 1. Shallow Clone (Mặc định)
|
|
111
|
-
|
|
112
|
-
```html
|
|
113
|
-
@let cloned = (user | LibsUiPipesCloneObjectPipe);
|
|
114
|
-
|
|
115
|
-
<p>Original: {{ user.name }}</p>
|
|
116
|
-
<p>Cloned: {{ cloned.name }}</p>
|
|
117
46
|
```
|
|
118
|
-
|
|
119
|
-
```typescript
|
|
120
|
-
user = { name: 'John', age: 30 };
|
|
121
|
-
// Shallow clone dùng spread: { ...user }
|
|
122
|
-
// Chỉnh sửa clone không ảnh hưởng original (top-level)
|
|
47
|
+
value | LibsUiPipesCloneObjectPipe : isCloneDeep? : defaultData?
|
|
123
48
|
```
|
|
124
49
|
|
|
125
|
-
|
|
50
|
+
| Tham số | Type | Bắt buộc | Default | Mô tả | Ví dụ |
|
|
51
|
+
|---|---|---|---|---|---|
|
|
52
|
+
| `data` | `any \| Signal<any>` | Có | — | Object, array hoặc Signal cần clone | `user \| LibsUiPipesCloneObjectPipe` |
|
|
53
|
+
| `isCloneDeep` | `boolean` | Không | `false` | `true` để deep clone toàn bộ nested structure; `false` để shallow clone | `user \| LibsUiPipesCloneObjectPipe : true` |
|
|
54
|
+
| `defaultData` | `any` | Không | `{}` | Giá trị trả về khi `data` là falsy | `val \| LibsUiPipesCloneObjectPipe : false : { name: 'Guest' }` |
|
|
126
55
|
|
|
127
|
-
|
|
128
|
-
@let cloned = (user | LibsUiPipesCloneObjectPipe : true);
|
|
56
|
+
**Return:** `any` — object/array đã được clone, hoặc `defaultData` nếu input là falsy.
|
|
129
57
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
58
|
+
## Ví dụ sử dụng
|
|
59
|
+
|
|
60
|
+
### 1. Shallow Clone (mặc định)
|
|
61
|
+
|
|
62
|
+
Phù hợp khi object chỉ có 1 cấp (không nested) hoặc cần hiệu năng cao.
|
|
133
63
|
|
|
134
64
|
```typescript
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
address: { city: 'HCM', country: 'VN' }
|
|
138
|
-
};
|
|
65
|
+
// component.ts
|
|
66
|
+
import { LibsUiPipesCloneObjectPipe } from '@libs-ui/pipes-clone-object';
|
|
139
67
|
|
|
140
|
-
|
|
141
|
-
|
|
68
|
+
@Component({
|
|
69
|
+
standalone: true,
|
|
70
|
+
imports: [LibsUiPipesCloneObjectPipe],
|
|
71
|
+
template: `
|
|
72
|
+
@let editUser = (user | LibsUiPipesCloneObjectPipe);
|
|
73
|
+
<p>Original: {{ user.name }}</p>
|
|
74
|
+
<p>Clone: {{ editUser.name }}</p>
|
|
75
|
+
`,
|
|
76
|
+
})
|
|
77
|
+
export class UserFormComponent {
|
|
78
|
+
user = { name: 'John', email: 'john@example.com' };
|
|
79
|
+
}
|
|
142
80
|
```
|
|
143
81
|
|
|
144
|
-
|
|
82
|
+
Shallow clone dùng spread `{ ...user }`. Thay đổi top-level property trên clone sẽ không ảnh hưởng object gốc. Tuy nhiên, nếu object có nested property (như `user.address`), hai bên vẫn dùng chung reference.
|
|
145
83
|
|
|
146
|
-
|
|
147
|
-
@let clonedItems = (items | LibsUiPipesCloneObjectPipe);
|
|
84
|
+
### 2. Deep Clone cho Nested Objects
|
|
148
85
|
|
|
149
|
-
|
|
150
|
-
@for (item of clonedItems; track item) {
|
|
151
|
-
<li>{{ item }}</li>
|
|
152
|
-
}
|
|
153
|
-
</ul>
|
|
154
|
-
```
|
|
86
|
+
Dùng khi object có nested structure và cần chỉnh sửa an toàn ở bất kỳ cấp nào.
|
|
155
87
|
|
|
156
88
|
```typescript
|
|
157
|
-
|
|
158
|
-
|
|
89
|
+
// component.ts
|
|
90
|
+
import { LibsUiPipesCloneObjectPipe } from '@libs-ui/pipes-clone-object';
|
|
91
|
+
|
|
92
|
+
@Component({
|
|
93
|
+
standalone: true,
|
|
94
|
+
imports: [LibsUiPipesCloneObjectPipe],
|
|
95
|
+
template: `
|
|
96
|
+
@let cloned = (company | LibsUiPipesCloneObjectPipe : true);
|
|
97
|
+
<p>CEO gốc: {{ company.ceo.name }}</p>
|
|
98
|
+
<p>CEO clone: {{ cloned.ceo.name }}</p>
|
|
99
|
+
`,
|
|
100
|
+
})
|
|
101
|
+
export class CompanyFormComponent {
|
|
102
|
+
company = {
|
|
103
|
+
name: 'Tech Corp',
|
|
104
|
+
ceo: { name: 'John', address: { city: 'HCM' } },
|
|
105
|
+
};
|
|
106
|
+
}
|
|
159
107
|
```
|
|
160
108
|
|
|
161
|
-
|
|
109
|
+
Truyền `true` làm tham số thứ hai để kích hoạt deep clone. Mọi cấp nested đều là object mới hoàn toàn — chỉnh sửa clone không ảnh hưởng gì đến original.
|
|
162
110
|
|
|
163
|
-
|
|
164
|
-
<!-- Không cần gọi userSignal().name -->
|
|
165
|
-
@let cloned = (userSignal | LibsUiPipesCloneObjectPipe);
|
|
166
|
-
<p>Name: {{ cloned.name }}</p>
|
|
167
|
-
```
|
|
111
|
+
### 3. Clone Array
|
|
168
112
|
|
|
169
113
|
```typescript
|
|
170
|
-
|
|
114
|
+
// component.ts
|
|
115
|
+
import { LibsUiPipesCloneObjectPipe } from '@libs-ui/pipes-clone-object';
|
|
171
116
|
|
|
172
|
-
|
|
117
|
+
@Component({
|
|
118
|
+
standalone: true,
|
|
119
|
+
imports: [LibsUiPipesCloneObjectPipe],
|
|
120
|
+
template: `
|
|
121
|
+
@let clonedItems = (items | LibsUiPipesCloneObjectPipe);
|
|
122
|
+
<ul>
|
|
123
|
+
@for (item of clonedItems; track item) {
|
|
124
|
+
<li>{{ item }}</li>
|
|
125
|
+
}
|
|
126
|
+
</ul>
|
|
127
|
+
`,
|
|
128
|
+
})
|
|
129
|
+
export class ListComponent {
|
|
130
|
+
items = ['apple', 'banana', 'orange'];
|
|
131
|
+
}
|
|
173
132
|
```
|
|
174
133
|
|
|
175
|
-
|
|
134
|
+
Pipe tự nhận diện array và dùng spread `[ ...arr ]` để clone. Push/splice trên `clonedItems` sẽ không thay đổi mảng `items` gốc.
|
|
176
135
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
```
|
|
136
|
+
### 4. Signal Auto-Unwrap
|
|
137
|
+
|
|
138
|
+
Pipe tự động phát hiện Angular Signal bằng `isSignal()` và gọi `data()` trước khi clone — không cần làm thủ công trong template.
|
|
181
139
|
|
|
182
140
|
```typescript
|
|
183
|
-
|
|
184
|
-
|
|
141
|
+
// component.ts
|
|
142
|
+
import { Component, signal } from '@angular/core';
|
|
143
|
+
import { LibsUiPipesCloneObjectPipe } from '@libs-ui/pipes-clone-object';
|
|
144
|
+
|
|
145
|
+
@Component({
|
|
146
|
+
standalone: true,
|
|
147
|
+
imports: [LibsUiPipesCloneObjectPipe],
|
|
148
|
+
template: `
|
|
149
|
+
@let user = (userSignal | LibsUiPipesCloneObjectPipe);
|
|
150
|
+
<p>Name: {{ user.name }}</p>
|
|
151
|
+
<p>Age: {{ user.age }}</p>
|
|
152
|
+
`,
|
|
153
|
+
})
|
|
154
|
+
export class ProfileComponent {
|
|
155
|
+
userSignal = signal({ name: 'John', age: 30 });
|
|
156
|
+
}
|
|
185
157
|
```
|
|
186
158
|
|
|
187
|
-
|
|
159
|
+
Không cần viết `userSignal()` trong template — pipe tự unwrap và clone.
|
|
188
160
|
|
|
189
|
-
###
|
|
161
|
+
### 5. Default Data cho Falsy Input
|
|
162
|
+
|
|
163
|
+
Dùng khi data có thể là `null` hoặc `undefined`, để tránh lỗi `Cannot read property of null` trong template.
|
|
190
164
|
|
|
191
165
|
```typescript
|
|
192
|
-
|
|
166
|
+
// component.ts
|
|
167
|
+
import { LibsUiPipesCloneObjectPipe } from '@libs-ui/pipes-clone-object';
|
|
168
|
+
|
|
169
|
+
@Component({
|
|
170
|
+
standalone: true,
|
|
171
|
+
imports: [LibsUiPipesCloneObjectPipe],
|
|
172
|
+
template: `
|
|
173
|
+
@let config = (apiConfig | LibsUiPipesCloneObjectPipe : false : defaultConfig);
|
|
174
|
+
<p>API URL: {{ config.apiUrl }}</p>
|
|
175
|
+
<p>Timeout: {{ config.timeout }}ms</p>
|
|
176
|
+
`,
|
|
177
|
+
})
|
|
178
|
+
export class SettingsComponent {
|
|
179
|
+
apiConfig: { apiUrl: string; timeout: number } | null = null;
|
|
180
|
+
defaultConfig = { apiUrl: '/api/v1', timeout: 5000 };
|
|
181
|
+
}
|
|
193
182
|
```
|
|
194
183
|
|
|
195
|
-
|
|
184
|
+
Khi `apiConfig` là `null`, pipe trả về clone của `defaultConfig`. Truyền `false` làm `isCloneDeep` (tham số thứ 2) để không nhầm với `defaultData`.
|
|
196
185
|
|
|
197
|
-
|
|
198
|
-
| ------------- | ----------------------- | -------- | --------------------------------------------------------- |
|
|
199
|
-
| `data` | `any \| Signal<any>` | - | Data cần clone (object, array, hoặc Signal). |
|
|
200
|
-
| `isCloneDeep` | `boolean \| undefined` | `false` | `true` để deep clone, `false` để shallow clone. |
|
|
201
|
-
| `defaultData` | `any \| undefined` | `{}` | Giá trị mặc định trả về nếu data falsy. |
|
|
186
|
+
### 6. Dùng trong TypeScript (không cần inject pipe)
|
|
202
187
|
|
|
203
|
-
|
|
188
|
+
Pipe là thin wrapper của `cloneDeep` từ `@libs-ui/utils`. Khi cần clone trong file `.ts`, import thẳng hàm đó — không cần inject pipe.
|
|
204
189
|
|
|
205
|
-
|
|
190
|
+
```typescript
|
|
191
|
+
import { cloneDeep } from '@libs-ui/utils';
|
|
192
|
+
|
|
193
|
+
// Shallow clone
|
|
194
|
+
const shallow = { ...this.user };
|
|
195
|
+
shallow.name = 'Jane'; // user.name không thay đổi (top-level)
|
|
206
196
|
|
|
207
|
-
|
|
197
|
+
// Deep clone
|
|
198
|
+
const deep = cloneDeep(this.user);
|
|
199
|
+
deep.address.city = 'HN'; // user.address.city không thay đổi
|
|
200
|
+
```
|
|
208
201
|
|
|
209
|
-
|
|
202
|
+
## Logic ẩn quan trọng
|
|
210
203
|
|
|
211
|
-
|
|
204
|
+
### Signal Auto-Unwrapping
|
|
212
205
|
|
|
213
206
|
```typescript
|
|
207
|
+
// Bên trong pipe
|
|
214
208
|
if (isSignal(data)) {
|
|
215
|
-
data = data(); //
|
|
209
|
+
data = data(); // tự động unwrap Signal
|
|
216
210
|
}
|
|
217
|
-
return isCloneDeep ? cloneDeep(data) : { ...data };
|
|
211
|
+
return isCloneDeep ? cloneDeep(data) : Array.isArray(data) ? [...data] : { ...data };
|
|
218
212
|
```
|
|
219
213
|
|
|
220
|
-
|
|
221
|
-
```typescript
|
|
222
|
-
// ❌ Không dùng pipe - phải unwrap thủ công
|
|
223
|
-
{{ userSignal().name }}
|
|
214
|
+
Pipe dùng `isSignal()` từ `@angular/core` để detect Signal tại runtime. Điều này hoạt động với cả `signal()`, `computed()` và `input()`.
|
|
224
215
|
|
|
225
|
-
|
|
226
|
-
@let cloned = (userSignal | LibsUiPipesCloneObjectPipe);
|
|
227
|
-
{{ cloned.name }}
|
|
228
|
-
```
|
|
216
|
+
### Shallow vs Deep — khi nào chọn cái nào
|
|
229
217
|
|
|
230
|
-
|
|
218
|
+
| Tiêu chí | Shallow (`false`) | Deep (`true`) |
|
|
219
|
+
|---|---|---|
|
|
220
|
+
| Cách thực hiện | `{ ...obj }` hoặc `[ ...arr ]` | `cloneDeep(obj)` từ lodash |
|
|
221
|
+
| Tốc độ | Nhanh | Chậm hơn |
|
|
222
|
+
| Memory | Ít | Nhiều hơn |
|
|
223
|
+
| An toàn nested | Không — nested share reference | Có — toàn bộ cây object mới |
|
|
224
|
+
| Nên dùng khi | Object phẳng, 1 cấp | Object có nested structure |
|
|
231
225
|
|
|
232
|
-
|
|
233
|
-
| ---- | -------- | ---- | ---- |
|
|
234
|
-
| Shallow | `{ ...obj }` hoặc `[ ...arr ]` | Nhanh, ít memory | Nested objects share reference |
|
|
235
|
-
| Deep | `cloneDeep(obj)` | An toàn toàn bộ | Chậm hơn, nhiều memory |
|
|
226
|
+
### Default data handling
|
|
236
227
|
|
|
237
|
-
|
|
238
|
-
// Shallow - nested vẫn share reference
|
|
239
|
-
const shallow = { ...user };
|
|
240
|
-
shallow.name = 'Jane'; // ✅ Không ảnh hưởng original
|
|
241
|
-
shallow.address.city = 'HN'; // ❌ Ảnh hưởng original!
|
|
242
|
-
|
|
243
|
-
// Deep - hoàn toàn independent
|
|
244
|
-
const deep = cloneDeep(user);
|
|
245
|
-
deep.address.city = 'HN'; // ✅ Không ảnh hưởng original
|
|
246
|
-
```
|
|
228
|
+
Pipe kiểm tra input với `if (!data)` — tất cả các giá trị falsy đều kích hoạt fallback:
|
|
247
229
|
|
|
248
|
-
|
|
230
|
+
```
|
|
231
|
+
transform(null) => defaultData ?? {}
|
|
232
|
+
transform(undefined) => defaultData ?? {}
|
|
233
|
+
transform(0) => defaultData ?? {}
|
|
234
|
+
transform('') => defaultData ?? {}
|
|
235
|
+
transform(false) => defaultData ?? {}
|
|
236
|
+
```
|
|
249
237
|
|
|
250
|
-
|
|
238
|
+
## Lưu ý quan trọng
|
|
251
239
|
|
|
252
|
-
|
|
253
|
-
// Các trường hợp return default
|
|
254
|
-
transform(null) => defaultData || {}
|
|
255
|
-
transform(undefined) => defaultData || {}
|
|
256
|
-
transform(0) => defaultData || {}
|
|
257
|
-
transform('') => defaultData || {}
|
|
258
|
-
|
|
259
|
-
// Với custom defaultData
|
|
260
|
-
transform(null, false, { name: 'Guest' })
|
|
261
|
-
// => { name: 'Guest' }
|
|
262
|
-
```
|
|
240
|
+
⚠️ **Shallow clone không bảo vệ nested objects**: Khi dùng shallow clone (mặc định), chỉ các property ở cấp đầu tiên là độc lập. Nested object vẫn dùng chung reference với original. Nếu cần chỉnh sửa property lồng sâu, bắt buộc dùng `isCloneDeep = true`.
|
|
263
241
|
|
|
264
|
-
|
|
242
|
+
⚠️ **Deep clone tốn tài nguyên hơn**: `cloneDeep` từ lodash clone toàn bộ cây object. Tránh dùng trong vòng lặp lớn hoặc với object cực kỳ lớn. Với list, cân nhắc chỉ clone khi thực sự cần thay đổi nested data.
|
|
265
243
|
|
|
266
|
-
|
|
244
|
+
⚠️ **Signal unwrap xảy ra trước khi clone**: Nếu input là `Signal<Signal<T>>` (signal lồng signal), chỉ cấp ngoài cùng được unwrap. Pipe không đệ quy unwrap nhiều cấp signal.
|
|
267
245
|
|
|
268
|
-
|
|
246
|
+
⚠️ **Không dùng `@libs-ui/exports`**: BẮT BUỘC import từ entrypoint cụ thể `@libs-ui/pipes-clone-object`, không import từ `@libs-ui/exports`.
|
|
269
247
|
|
|
270
|
-
|
|
248
|
+
## Demo
|
|
271
249
|
|
|
272
|
-
|
|
250
|
+
```bash
|
|
251
|
+
npx nx serve core-ui
|
|
252
|
+
```
|
|
273
253
|
|
|
274
|
-
|
|
254
|
+
Truy cập: http://localhost:4500/pipes/clone-object
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"libs-ui-pipes-clone-object.mjs","sources":["../../../../../libs-ui/pipes/clone-object/src/clone-object.component.ts","../../../../../libs-ui/pipes/clone-object/src/libs-ui-pipes-clone-object.ts"],"sourcesContent":["import { isSignal, Pipe, PipeTransform } from '@angular/core';\nimport { cloneDeep } from '@libs-ui/utils';\n@Pipe({\n name: 'LibsUiPipesCloneObjectPipe',\n standalone: true,\n})\nexport class LibsUiPipesCloneObjectPipe implements PipeTransform {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n transform(data: any, isCloneDeep?: boolean, defaultData?: any): any {\n if (!data) {\n return defaultData ?? {};\n }\n\n if (isSignal(data)) {\n data = data();\n }\n\n if (isCloneDeep) {\n return cloneDeep(data);\n }\n\n return Array.isArray(data) ? [...data] : { ...data };\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;MAMa,0BAA0B,CAAA;;AAErC,IAAA,SAAS,CAAC,IAAS,EAAE,WAAqB,EAAE,WAAiB,EAAA;QAC3D,IAAI,CAAC,IAAI,EAAE;YACT,OAAO,WAAW,IAAI,EAAE;
|
|
1
|
+
{"version":3,"file":"libs-ui-pipes-clone-object.mjs","sources":["../../../../../libs-ui/pipes/clone-object/src/clone-object.component.ts","../../../../../libs-ui/pipes/clone-object/src/libs-ui-pipes-clone-object.ts"],"sourcesContent":["import { isSignal, Pipe, PipeTransform } from '@angular/core';\nimport { cloneDeep } from '@libs-ui/utils';\n@Pipe({\n name: 'LibsUiPipesCloneObjectPipe',\n standalone: true,\n})\nexport class LibsUiPipesCloneObjectPipe implements PipeTransform {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n transform(data: any, isCloneDeep?: boolean, defaultData?: any): any {\n if (!data) {\n return defaultData ?? {};\n }\n\n if (isSignal(data)) {\n data = data();\n }\n\n if (isCloneDeep) {\n return cloneDeep(data);\n }\n\n return Array.isArray(data) ? [...data] : { ...data };\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;MAMa,0BAA0B,CAAA;;AAErC,IAAA,SAAS,CAAC,IAAS,EAAE,WAAqB,EAAE,WAAiB,EAAA;QAC3D,IAAI,CAAC,IAAI,EAAE;YACT,OAAO,WAAW,IAAI,EAAE,CAAC;SAC1B;AAED,QAAA,IAAI,QAAQ,CAAC,IAAI,CAAC,EAAE;YAClB,IAAI,GAAG,IAAI,EAAE,CAAC;SACf;QAED,IAAI,WAAW,EAAE;AACf,YAAA,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;SACxB;QAED,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC;KACtD;wGAhBU,0BAA0B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,IAAA,EAAA,CAAA,CAAA;sGAA1B,0BAA0B,EAAA,YAAA,EAAA,IAAA,EAAA,IAAA,EAAA,4BAAA,EAAA,CAAA,CAAA;;4FAA1B,0BAA0B,EAAA,UAAA,EAAA,CAAA;kBAJtC,IAAI;AAAC,YAAA,IAAA,EAAA,CAAA;AACJ,oBAAA,IAAI,EAAE,4BAA4B;AAClC,oBAAA,UAAU,EAAE,IAAI;AACjB,iBAAA,CAAA;;;ACLD;;AAEG;;;;"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@libs-ui/pipes-clone-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-clone-object.mjs",
|