@libs-ui/pipes-clone-object 0.2.355-9 → 0.2.356-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.
Files changed (2) hide show
  1. package/README.md +273 -2
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -1,3 +1,274 @@
1
- # pipes-clone-object
1
+ # @libs-ui/pipes-clone-object
2
2
 
3
- This library was generated with [Nx](https://nx.dev).
3
+ > Version: `0.2.355-15`
4
+ >
5
+ > Pipe clone object/array với hỗ trợ shallow clone, deep clone và xử lý Signal tự động.
6
+
7
+ ## Giới thiệu
8
+
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 thờ tự động unwrap Signal nếu input là Signal.
10
+
11
+ ### Tính năng
12
+
13
+ - ✅ Shallow clone với spread operator (nhanh)
14
+ - ✅ Deep clone với lodash cloneDeep (an toàn nested)
15
+ - ✅ Auto-unwrap Signal (không cần gọi `.()`)
16
+ - ✅ Default data khi input falsy
17
+ - ✅ Hoạt động với cả object và array
18
+ - ✅ Standalone pipe (Angular 16+)
19
+
20
+ ## Khi nào sử dụng
21
+
22
+ ### 📝 Form Editing (với Cancel functionality)
23
+
24
+ Khi bạn cần chỉnh sửa data trong form nhưng muốn có khả năng Cancel để khôi phục data gốc:
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 `{}`.
88
+
89
+ ## Cài đặt
90
+
91
+ ```bash
92
+ npm install @libs-ui/pipes-clone-object
93
+ ```
94
+
95
+ ## Import
96
+
97
+ ```typescript
98
+ import { LibsUiPipesCloneObjectPipe } from '@libs-ui/pipes-clone-object';
99
+
100
+ @Component({
101
+ standalone: true,
102
+ imports: [LibsUiPipesCloneObjectPipe],
103
+ // ...
104
+ })
105
+ export class YourComponent {}
106
+ ```
107
+
108
+ ## Ví dụ
109
+
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
+ ```
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)
123
+ ```
124
+
125
+ ### 2. Deep Clone
126
+
127
+ ```html
128
+ @let cloned = (user | LibsUiPipesCloneObjectPipe : true);
129
+
130
+ <p>City: {{ cloned.address.city }}</p>
131
+ <button (click)="cloned.address.city = 'HN'">Edit</button>
132
+ ```
133
+
134
+ ```typescript
135
+ user = {
136
+ name: 'John',
137
+ address: { city: 'HCM', country: 'VN' }
138
+ };
139
+
140
+ // Deep clone - address là object mới hoàn toàn
141
+ // An toàn khi edit nested properties
142
+ ```
143
+
144
+ ### 3. Clone Array
145
+
146
+ ```html
147
+ @let clonedItems = (items | LibsUiPipesCloneObjectPipe);
148
+
149
+ <ul>
150
+ @for (item of clonedItems; track item) {
151
+ <li>{{ item }}</li>
152
+ }
153
+ </ul>
154
+ ```
155
+
156
+ ```typescript
157
+ items = ['apple', 'banana', 'orange'];
158
+ // Array clone dùng spread: [...items]
159
+ ```
160
+
161
+ ### 4. 🔄 Signal Auto-Unwrap
162
+
163
+ ```html
164
+ <!-- Không cần gọi userSignal().name -->
165
+ @let cloned = (userSignal | LibsUiPipesCloneObjectPipe);
166
+ <p>Name: {{ cloned.name }}</p>
167
+ ```
168
+
169
+ ```typescript
170
+ userSignal = signal({ name: 'John', age: 30 });
171
+
172
+ // Pipe tự động detect Signal và unwrap
173
+ ```
174
+
175
+ ### 5. Default Data
176
+
177
+ ```html
178
+ @let result = (maybeNull | LibsUiPipesCloneObjectPipe : false : { name: 'Default' });
179
+ <p>Result: {{ result.name }}</p>
180
+ ```
181
+
182
+ ```typescript
183
+ maybeNull = null;
184
+ // Trả về { name: 'Default' } vì input là null
185
+ ```
186
+
187
+ ## API
188
+
189
+ ### LibsUiPipesCloneObjectPipe
190
+
191
+ ```typescript
192
+ data | LibsUiPipesCloneObjectPipe : isCloneDeep? : defaultData?
193
+ ```
194
+
195
+ #### Parameters
196
+
197
+ | Property | Type | Default | Description |
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. |
202
+
203
+ #### Returns
204
+
205
+ `any` - Object/array đã được clone (hoặc defaultData nếu input falsy)
206
+
207
+ ## Hidden Logic
208
+
209
+ ### 1. 🔄 Signal Auto-Unwrapping
210
+
211
+ Pipe sử dụng `isSignal()` từ @angular/core để detect và auto-unwrap Signal:
212
+
213
+ ```typescript
214
+ if (isSignal(data)) {
215
+ data = data(); // Auto-unwrap
216
+ }
217
+ return isCloneDeep ? cloneDeep(data) : { ...data };
218
+ ```
219
+
220
+ **So sánh:**
221
+ ```typescript
222
+ // ❌ Không dùng pipe - phải unwrap thủ công
223
+ {{ userSignal().name }}
224
+
225
+ // ✅ Dùng pipe - auto unwrap
226
+ @let cloned = (userSignal | LibsUiPipesCloneObjectPipe);
227
+ {{ cloned.name }}
228
+ ```
229
+
230
+ ### 2. 🔀 Shallow vs Deep Clone
231
+
232
+ | Loại | Cách làm | Pros | Cons |
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 |
236
+
237
+ ```typescript
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
+ ```
247
+
248
+ ### 3. 📦 Default Data Handling
249
+
250
+ Khi input là falsy, pipe trả về defaultData hoặc empty object:
251
+
252
+ ```typescript
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
+ ```
263
+
264
+ ## Demo
265
+
266
+ - **Local Development**: [http://localhost:4500/pipes/clone-object](http://localhost:4500/pipes/clone-object)
267
+
268
+ ## Unit Tests
269
+
270
+ Xem file `test-commands.md` để biết cách chạy unit tests.
271
+
272
+ ## License
273
+
274
+ MIT
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@libs-ui/pipes-clone-object",
3
- "version": "0.2.355-9",
3
+ "version": "0.2.356-1",
4
4
  "peerDependencies": {
5
5
  "@angular/core": ">=18.0.0",
6
- "@libs-ui/utils": "0.2.355-9"
6
+ "@libs-ui/utils": "0.2.356-1"
7
7
  },
8
8
  "sideEffects": false,
9
9
  "module": "fesm2022/libs-ui-pipes-clone-object.mjs",