What's alternative to angular.copy in Angular

Angular

Angular Problem Overview


How can I copy an object and lose its reference in Angular?

With AngularJS, I can use angular.copy(object), but I'm getting some error using that in Angular.

> EXCEPTION: ReferenceError: angular is not defined

Angular Solutions


Solution 1 - Angular

Assuming you are using ES6, you can use var copy = Object.assign({}, original). Works in modern browsers; if you need to support older browsers check out this polyfill

update:

With TypeScript 2.1+, ES6 shorthand object spread notation is available:

const copy = { ...original }

Solution 2 - Angular

Till we have a better solution, you can use the following:

duplicateObject = <YourObjType> JSON.parse(JSON.stringify(originalObject));

EDIT: Clarification

Please note: The above solution was only meant to be a quick-fix one liner, provided at a time when Angular 2 was under active development. My hope was we might eventually get an equivalent of angular.copy(). Therefore I did not want to write or import a deep-cloning library.

This method also has issues with parsing date properties (it will become a string).

Please do not use this method in production apps. Use it only in your experimental projects - the ones you are doing for learning Angular 2.

Solution 3 - Angular

The alternative for deep copying objects having nested objects inside is by using lodash's cloneDeep method.

For Angular, you can do it like this:

Install lodash with yarn add lodash or npm install lodash.

In your component, import cloneDeep and use it:

import { cloneDeep } from "lodash";
...
clonedObject = cloneDeep(originalObject);

It's only 18kb added to your build, well worth for the benefits.

I've also written an article here, if you need more insight on why using lodash's cloneDeep.

Solution 4 - Angular

For shallow copying you could use Object.assign which is a ES6 feature

let x = { name: 'Marek', age: 20 };
let y = Object.assign({}, x);
x === y; //false

DO NOT use it for deep cloning

Solution 5 - Angular

Use lodash as bertandg indicated. The reason angular no longer has this method, is because angular 1 was a stand-alone framework, and external libraries often ran into issues with the angular execution context. Angular 2 does not have that problem, so use whatever library that you want.

https://lodash.com/docs#cloneDeep

Solution 6 - Angular

If you want to copy a class instance, you can use Object.assign too, but you need to pass a new instance as a first parameter (instead of {}) :

class MyClass {
    public prop1: number;
    public prop2: number;

    public summonUnicorn(): void {
        alert('Unicorn !');
    }
}

let instance = new MyClass();
instance.prop1 = 12;
instance.prop2 = 42;

let wrongCopy = Object.assign({}, instance);
console.log(wrongCopy.prop1); // 12
console.log(wrongCopy.prop2); // 42
wrongCopy.summonUnicorn() // ERROR : undefined is not a function

let goodCopy = Object.assign(new MyClass(), instance);
console.log(goodCopy.prop1); // 12
console.log(goodCopy.prop2); // 42
goodCopy.summonUnicorn() // It works !

Solution 7 - Angular

The simplest solution I've found is:

let yourDeepCopiedObject = _.cloneDeep(yourOriginalObject);

*IMPORTANT STEPS: You must install lodash to use this (which was unclear from other answers):

$ npm install --save lodash

$ npm install --save @types/lodash

and then import it in your ts file:

import * as _ from "lodash";

Solution 8 - Angular

As others have already pointed out, using lodash or underscore is probably the best solution. But if you do not need those libraries for anything else, you can probably use something like this:

  function deepClone(obj) {

    // return value is input is not an Object or Array.
    if (typeof(obj) !== 'object' || obj === null) {
      return obj;    
    }

    let clone;

    if(Array.isArray(obj)) {
      clone = obj.slice();  // unlink Array reference.
    } else {
      clone = Object.assign({}, obj); // Unlink Object reference.
    }

    let keys = Object.keys(clone);

    for (let i=0; i<keys.length; i++) {
      clone[keys[i]] = deepClone(clone[keys[i]]); // recursively unlink reference to nested objects.
    }

    return clone; // return unlinked clone.

  }

That's what we decided to do.

Solution 9 - Angular

let newObj = JSON.parse(JSON.stringify(obj))

The JSON.stringify() method converts a JavaScript object or value to a JSON string

Solution 10 - Angular

I have created a service to use with Angular 5 or higher, it uses the angular.copy() the base of angularjs, it works well for me. Additionally, there are other functions like isUndefined, etc. I hope it helps. Like any optimization, it would be nice to know. regards

import { Injectable } from '@angular/core';

@Injectable({providedIn: 'root'})
export class AngularService {

  private TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/;
  private stackSource = [];
  private stackDest = [];

  constructor() { }

  public isNumber(value: any): boolean {
    if ( typeof value === 'number' ) { return true; }
    else { return false; }
  }

  public isTypedArray(value: any) {
    return value && this.isNumber(value.length) && this.TYPED_ARRAY_REGEXP.test(toString.call(value));
  }

  public isArrayBuffer(obj: any) {
    return toString.call(obj) === '[object ArrayBuffer]';
  }

  public isUndefined(value: any) {return typeof value === 'undefined'; }

  public isObject(value: any) {  return value !== null && typeof value === 'object'; }

  public isBlankObject(value: any) {
    return value !== null && typeof value === 'object' && !Object.getPrototypeOf(value);
  }

  public isFunction(value: any) { return typeof value === 'function'; }

  public setHashKey(obj: any, h: any) {
    if (h) { obj.$$hashKey = h; }
    else { delete obj.$$hashKey; }
  }

  private isWindow(obj: any) { return obj && obj.window === obj; }

  private isScope(obj: any) { return obj && obj.$evalAsync && obj.$watch; }


  private copyRecurse(source: any, destination: any) {

    const h = destination.$$hashKey;

    if (Array.isArray(source)) {
      for (let i = 0, ii = source.length; i < ii; i++) {
        destination.push(this.copyElement(source[i]));
      }
    } else if (this.isBlankObject(source)) {
      for (const key of Object.keys(source)) {
        destination[key] = this.copyElement(source[key]);
      }
    } else if (source && typeof source.hasOwnProperty === 'function') {
      for (const key of Object.keys(source)) {
        destination[key] = this.copyElement(source[key]);
      }
    } else {
      for (const key of Object.keys(source)) {
        destination[key] = this.copyElement(source[key]);
      }
    }
    this.setHashKey(destination, h);
    return destination;
  }

  private copyElement(source: any) {

    if (!this.isObject(source)) {
      return source;
    }

    const index = this.stackSource.indexOf(source);

    if (index !== -1) {
      return this.stackDest[index];
    }

    if (this.isWindow(source) || this.isScope(source)) {
      throw console.log('Cant copy! Making copies of Window or Scope instances is not supported.');
    }

    let needsRecurse = false;
    let destination = this.copyType(source);

    if (destination === undefined) {
      destination = Array.isArray(source) ? [] : Object.create(Object.getPrototypeOf(source));
      needsRecurse = true;
    }

    this.stackSource.push(source);
    this.stackDest.push(destination);

    return needsRecurse
      ? this.copyRecurse(source, destination)
      : destination;
  }

  private copyType = (source: any) => {

    switch (toString.call(source)) {
      case '[object Int8Array]':
      case '[object Int16Array]':
      case '[object Int32Array]':
      case '[object Float32Array]':
      case '[object Float64Array]':
      case '[object Uint8Array]':
      case '[object Uint8ClampedArray]':
      case '[object Uint16Array]':
      case '[object Uint32Array]':
        return new source.constructor(this.copyElement(source.buffer), source.byteOffset, source.length);

      case '[object ArrayBuffer]':
        if (!source.slice) {
          const copied = new ArrayBuffer(source.byteLength);
          new Uint8Array(copied).set(new Uint8Array(source));
          return copied;
        }
        return source.slice(0);

      case '[object Boolean]':
      case '[object Number]':
      case '[object String]':
      case '[object Date]':
        return new source.constructor(source.valueOf());

      case '[object RegExp]':
        const re = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
        re.lastIndex = source.lastIndex;
        return re;

      case '[object Blob]':
        return new source.constructor([source], {type: source.type});
    }

    if (this.isFunction(source.cloneNode)) {
      return source.cloneNode(true);
    }
  }

  public copy(source: any, destination?: any) {

    if (destination) {
      if (this.isTypedArray(destination) || this.isArrayBuffer(destination)) {
        throw console.log('Cant copy! TypedArray destination cannot be mutated.');
      }
      if (source === destination) {
        throw console.log('Cant copy! Source and destination are identical.');
      }

      if (Array.isArray(destination)) {
        destination.length = 0;
      } else {
        destination.forEach((value: any, key: any) => {
          if (key !== '$$hashKey') {
            delete destination[key];
          }
        });
      }

      this.stackSource.push(source);
      this.stackDest.push(destination);
      return this.copyRecurse(source, destination);
    }

    return this.copyElement(source);
  }
}

Solution 11 - Angular

You can clone the Array like

 this.assignCustomerList = Object.assign([], this.customerList);

And clone the object like

this.assignCustomer = Object.assign({}, this.customer);

Solution 12 - Angular

I needed this feature just form my app 'models' (raw backend data converted to objects). So I ended up using a combination of Object.create (create new object from specified prototype) and Object.assign (copy properties between objects). Need to handle the deep copy manually. I created a gist for this.

Solution 13 - Angular

If you are open to using an npm pacakage, you can try out "ngx-scv-util":

Install the package by running following command:

npm i ngx-scv-util --save

Import the package into your component using:

import { NgxScvUtil } from "ngx-scv-util";
....
constructor(private util: NgxScvUtil) {}

Usage:

let newObject = this.util.deepCopy(originalObject);

It is a very light package. More information is available here: https://www.npmjs.com/package/ngx-scv-util

Solution 14 - Angular

Had the same Issue, and didn't wanna use any plugins just for deep cloning:

static deepClone(object): any {
        const cloneObj = (<any>object.constructor());
        const attributes = Object.keys(object);
        for (const attribute of attributes) {
            const property = object[attribute];

            if (typeof property === 'object') {
                cloneObj[attribute] = this.deepClone(property);
            } else {
                cloneObj[attribute] = property;
            }
        }
        return cloneObj;
    }

Credits: I made this function more readable, please check the exceptions to its functionality below

Solution 15 - Angular

I as well as you faced a problem of work angular.copy and angular.expect because they do not copy the object or create the object without adding some dependencies. My solution was this:

  copyFactory = (() ->
    resource = ->
      resource.__super__.constructor.apply this, arguments
      return
    this.extendTo resource
    resource
  ).call(factory)

Solution 16 - Angular

If you are not already using lodash I wouldn't recommend installing it just for this one method. I suggest instead a more narrowly specialized library such as 'clone':

npm install clone

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRodrigo RealView Question on Stackoverflow
Solution 1 - AngularSasxaView Answer on Stackoverflow
Solution 2 - AngularManiView Answer on Stackoverflow
Solution 3 - AngularBogdanCView Answer on Stackoverflow
Solution 4 - AngularmareksView Answer on Stackoverflow
Solution 5 - AngularLanderVView Answer on Stackoverflow
Solution 6 - AngularGuillaume NuryView Answer on Stackoverflow
Solution 7 - AngularKyle KrzeskiView Answer on Stackoverflow
Solution 8 - AngularSpitfireView Answer on Stackoverflow
Solution 9 - AngularCharithJView Answer on Stackoverflow
Solution 10 - AngularCésar HolmesView Answer on Stackoverflow
Solution 11 - AngularPadmanabhan VeluView Answer on Stackoverflow
Solution 12 - AngularmppfilesView Answer on Stackoverflow
Solution 13 - AngularSharathView Answer on Stackoverflow
Solution 14 - AngularLuc BucherView Answer on Stackoverflow
Solution 15 - AngularViktor IvliievView Answer on Stackoverflow
Solution 16 - AngularReuben SivanView Answer on Stackoverflow