npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@libs-ui/pipes-clone-object

v0.2.357-2

Published

> Angular pipe clone object/array với hỗ trợ shallow clone, deep clone và tự động unwrap Signal.

Readme

@libs-ui/pipes-clone-object

Angular pipe clone object/array với hỗ trợ shallow clone, deep clone và tự động unwrap Signal.

Giới thiệu

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.

Tính năng

  • ✅ Shallow clone với spread operator ({ ...obj } / [ ...arr ]) — nhanh, ít memory
  • ✅ Deep clone với cloneDeep từ @libs-ui/utils — an toàn cho nested objects
  • ✅ Tự động unwrap Signal — không cần gọi .() trong template
  • ✅ Default data khi input là falsy (null, undefined, 0, "", false)
  • ✅ Hoạt động với cả object và array
  • ✅ Standalone pipe (Angular 16+), không cần NgModule

Khi nào sử dụng

  • 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.
  • 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.
  • Undo/Redo Functionality: Lưu trữ các snapshot state cũ để có thể khôi phục.
  • 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.

Cài đặt

npm install @libs-ui/pipes-clone-object

Import

import { LibsUiPipesCloneObjectPipe } from '@libs-ui/pipes-clone-object';

@Component({
  standalone: true,
  imports: [LibsUiPipesCloneObjectPipe],
  // ...
})
export class YourComponent {}

Transform

value | LibsUiPipesCloneObjectPipe : isCloneDeep? : defaultData?

| Tham số | Type | Bắt buộc | Default | Mô tả | Ví dụ | |---|---|---|---|---|---| | data | any \| Signal<any> | Có | — | Object, array hoặc Signal cần clone | user \| LibsUiPipesCloneObjectPipe | | isCloneDeep | boolean | Không | false | true để deep clone toàn bộ nested structure; false để shallow clone | user \| LibsUiPipesCloneObjectPipe : true | | defaultData | any | Không | {} | Giá trị trả về khi data là falsy | val \| LibsUiPipesCloneObjectPipe : false : { name: 'Guest' } |

Return: any — object/array đã được clone, hoặc defaultData nếu input là falsy.

Ví dụ sử dụng

1. Shallow Clone (mặc định)

Phù hợp khi object chỉ có 1 cấp (không nested) hoặc cần hiệu năng cao.

// component.ts
import { LibsUiPipesCloneObjectPipe } from '@libs-ui/pipes-clone-object';

@Component({
  standalone: true,
  imports: [LibsUiPipesCloneObjectPipe],
  template: `
    @let editUser = (user | LibsUiPipesCloneObjectPipe);
    <p>Original: {{ user.name }}</p>
    <p>Clone: {{ editUser.name }}</p>
  `,
})
export class UserFormComponent {
  user = { name: 'John', email: '[email protected]' };
}

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.

2. Deep Clone cho Nested Objects

Dùng khi object có nested structure và cần chỉnh sửa an toàn ở bất kỳ cấp nào.

// component.ts
import { LibsUiPipesCloneObjectPipe } from '@libs-ui/pipes-clone-object';

@Component({
  standalone: true,
  imports: [LibsUiPipesCloneObjectPipe],
  template: `
    @let cloned = (company | LibsUiPipesCloneObjectPipe : true);
    <p>CEO gốc: {{ company.ceo.name }}</p>
    <p>CEO clone: {{ cloned.ceo.name }}</p>
  `,
})
export class CompanyFormComponent {
  company = {
    name: 'Tech Corp',
    ceo: { name: 'John', address: { city: 'HCM' } },
  };
}

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.

3. Clone Array

// component.ts
import { LibsUiPipesCloneObjectPipe } from '@libs-ui/pipes-clone-object';

@Component({
  standalone: true,
  imports: [LibsUiPipesCloneObjectPipe],
  template: `
    @let clonedItems = (items | LibsUiPipesCloneObjectPipe);
    <ul>
      @for (item of clonedItems; track item) {
        <li>{{ item }}</li>
      }
    </ul>
  `,
})
export class ListComponent {
  items = ['apple', 'banana', 'orange'];
}

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.

4. Signal Auto-Unwrap

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.

// component.ts
import { Component, signal } from '@angular/core';
import { LibsUiPipesCloneObjectPipe } from '@libs-ui/pipes-clone-object';

@Component({
  standalone: true,
  imports: [LibsUiPipesCloneObjectPipe],
  template: `
    @let user = (userSignal | LibsUiPipesCloneObjectPipe);
    <p>Name: {{ user.name }}</p>
    <p>Age: {{ user.age }}</p>
  `,
})
export class ProfileComponent {
  userSignal = signal({ name: 'John', age: 30 });
}

Không cần viết userSignal() trong template — pipe tự unwrap và clone.

5. Default Data cho Falsy Input

Dùng khi data có thể là null hoặc undefined, để tránh lỗi Cannot read property of null trong template.

// component.ts
import { LibsUiPipesCloneObjectPipe } from '@libs-ui/pipes-clone-object';

@Component({
  standalone: true,
  imports: [LibsUiPipesCloneObjectPipe],
  template: `
    @let config = (apiConfig | LibsUiPipesCloneObjectPipe : false : defaultConfig);
    <p>API URL: {{ config.apiUrl }}</p>
    <p>Timeout: {{ config.timeout }}ms</p>
  `,
})
export class SettingsComponent {
  apiConfig: { apiUrl: string; timeout: number } | null = null;
  defaultConfig = { apiUrl: '/api/v1', timeout: 5000 };
}

Khi apiConfignull, pipe trả về clone của defaultConfig. Truyền false làm isCloneDeep (tham số thứ 2) để không nhầm với defaultData.

6. Dùng trong TypeScript (không cần inject pipe)

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.

import { cloneDeep } from '@libs-ui/utils';

// Shallow clone
const shallow = { ...this.user };
shallow.name = 'Jane'; // user.name không thay đổi (top-level)

// Deep clone
const deep = cloneDeep(this.user);
deep.address.city = 'HN'; // user.address.city không thay đổi

Logic ẩn quan trọng

Signal Auto-Unwrapping

// Bên trong pipe
if (isSignal(data)) {
  data = data(); // tự động unwrap Signal
}
return isCloneDeep ? cloneDeep(data) : Array.isArray(data) ? [...data] : { ...data };

Pipe dùng isSignal() từ @angular/core để detect Signal tại runtime. Điều này hoạt động với cả signal(), computed()input().

Shallow vs Deep — khi nào chọn cái nào

| Tiêu chí | Shallow (false) | Deep (true) | |---|---|---| | Cách thực hiện | { ...obj } hoặc [ ...arr ] | cloneDeep(obj) từ lodash | | Tốc độ | Nhanh | Chậm hơn | | Memory | Ít | Nhiều hơn | | An toàn nested | Không — nested share reference | Có — toàn bộ cây object mới | | Nên dùng khi | Object phẳng, 1 cấp | Object có nested structure |

Default data handling

Pipe kiểm tra input với if (!data) — tất cả các giá trị falsy đều kích hoạt fallback:

transform(null)      => defaultData ?? {}
transform(undefined) => defaultData ?? {}
transform(0)         => defaultData ?? {}
transform('')        => defaultData ?? {}
transform(false)     => defaultData ?? {}

Lưu ý quan trọng

⚠️ 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.

⚠️ 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.

⚠️ 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.

⚠️ 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.

Demo

npx nx serve core-ui

Truy cập: http://localhost:4500/pipes/clone-object