Angular'da angular.copy'ye alternatif nedir


136

Bir nesneyi nasıl kopyalayabilirim ve Angular'daki referansını nasıl kaybedebilirim?

AngularJS ile kullanabilirim angular.copy(object), ancak bunu Angular'da kullanırken bazı hatalar alıyorum.

İSTİSNA: ReferenceError: angulartanımlı değil



Birçok durumda kullanmak isteyebilirsiniz .copy()ama aslında ihtiyacınız olmaz. Gördüğüm çeşitli AngJS1 projelerinde, daha temiz bir kod için ilgili alt yapıların manuel bir kopyasının yapılacağı bir aşırılıktı. Belki de bu, Angular ekibi tarafından uygulanmama kararının bir parçasıydı.
phil294

bu arada, ilgili (ve ayrıca cevaplanmamış): stackoverflow.com/questions/41124528/…
phil294

Yanıtlar:


180

ES6 kullandığınızı varsayarsak kullanabilirsiniz var copy = Object.assign({}, original). Modern tarayıcılarda çalışır; eski tarayıcıları desteklemeniz gerekiyorsa bu çoklu dolguyu kontrol edin

Güncelleme:

TypeScript 2.1+ ile ES6 kısaltılmış nesne yayılma gösterimi mevcuttur:

const copy = { ...original }

75
Not angular.copy()derin kopya aykırı oluşturur Object.assign(). Derin kopya istiyorsanız lodash _.cloneDeep(value) lodash.com/docs#cloneDeep'i
bertrandg

Webstorm'da aldım Unresolved function or method assign(); IDE Ayrıntıları: Webstorm 2016.2. Bunu nasıl çözebilirim?
mihai

2
@meorfi gidin File -> Settings -> Languages & Frameworks -> Javascriptve seti Javascript language versioniçin ECMAScript 6.0.
Siri0S

@bertrandg _.clone (değer), angular.copy () 'den farklıdır, yeni örnek oluşturmaz, bu nedenle _.cloneDeep (değer) bir referans stackoverflow.com/questions/26411754/…
Zealitude

5
Ek olarak, bir dizi kopyalıyorsanız, şunu kullanın:const copy = [ ...original ]
daleyjem

43

Daha iyi bir çözüme sahip olana kadar aşağıdakileri kullanabilirsiniz:

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

DÜZENLEME: Açıklama

Lütfen dikkat: Yukarıdaki çözüm, Angular 2'nin aktif geliştirme aşamasındayken sağlanan, yalnızca hızlı bir şekilde düzeltilebilen bir astar olarak düşünülmüştür. Umudum, sonunda bir eşdeğerini elde edebilmekti angular.copy(). Bu nedenle bir derin klonlama kitaplığı yazmak veya içeri aktarmak istemedim.

Bu yöntemin tarih özelliklerinin ayrıştırılmasıyla ilgili sorunları da vardır (bir dizge olacaktır).

Lütfen bu yöntemi üretim uygulamalarında kullanmayın . Bunu yalnızca deneysel projelerinizde kullanın - Angular 2'yi öğrenmek için yaptığınız projelerde.


11
bu senin tarihlerini mahvediyor ve cehennem kadar yavaş.
LanderV

5
Tek bir görevi yerine getirmek için tüm bir kütüphaneyi içe aktarmak kadar yavaş değil, yaptığınız şey oldukça basit olduğu sürece ...
Ian Belcher

1
bu korkunç, asla onu kullanma
Murhaf Sousli

1
@MurhafSousli lütfen bu cevabın içeriğini anlamaya çalışın. Bu, Angular 2 geliştirme aşamasındayken sağlandı ve umut, sonunda eşdeğer bir angular.copy () işlevi elde edeceğimizdi. Bekleme süresini köprülemek için, daha iyi bir çözüm bulana kadar bu çözümü geçici bir seçenek olarak koydum. Bu derin klonlama ile tek astarlı. Bu korkunç , katılıyorum ... Ama o zamanki deneysel bağlam göz önüne alındığında, o kadar da kötü değil.
Mani

1
@ LazarLjubenović elbette 2018'de durum böyledir ve bugün size tamamen katılıyorum , ancak 2016'da web paketinde ağaç sallama yoktu, bu nedenle çoğu durumda tüm bir kütüphaneyi içe aktarırsınız.
Ian Belcher

22

İç içe nesneler içeren nesneleri derinlemesine kopyalamanın alternatifi, lodash'ın cloneDeep yöntemini kullanmaktır.

Angular için bunu şu şekilde yapabilirsiniz:

Lodash'ı yarn add lodashveya ile yükleyin npm install lodash.

Bileşeninizde, cloneDeeponu içe aktarın ve kullanın:

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

Yapınıza yalnızca 18kb eklenmiştir, faydalara değer.

Ayrıca Lodash'ın cloneDeep'i neden kullandığınıza dair daha fazla bilgiye ihtiyacınız varsa, burada bir makale yazdım.


2
Nesneleri derinlemesine kopyalayabilmek için çıktıya "yalnızca 18kb" eklendi mi? JavaScript tam bir karmaşa.
Endrju

Başvurulan makalenizi okuduktan sonra, cloneDeepyöntemin yeni bir nesne başlattığını anladım . Zaten bir hedef nesnemiz varsa onu yine de kullanmalı mıyız?
Stephane

17

İçin sığ bir ES6 özelliğidir Eğer Object.assign kullanabilirsiniz kopyalama

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

Derin klonlama için KULLANMAYIN


3
Derin klonlama için ne kullanılabilir?
DB

15

Lodash'ı belirtildiği gibi kullanın. Açısal'nın artık bu yönteme sahip olmamasının nedeni, açısal 1'in bağımsız bir çerçeve olması ve dış kütüphanelerin genellikle açısal yürütme bağlamıyla ilgili sorunlarla karşılaşmasıdır. Angular 2'de bu sorun yok, bu yüzden istediğiniz kütüphaneyi kullanın.

https://lodash.com/docs#cloneDeep


8

Bir sınıf örneğini kopyalamak istiyorsanız, Object.assign'ı da kullanabilirsiniz, ancak yeni bir örneği ilk parametre olarak geçirmeniz gerekir ({} yerine):

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 !

8

Bulduğum en basit çözüm:

let yourDeepCopiedObject = _.cloneDeep(yourOriginalObject);

* ÖNEMLİ ADIMLAR: Bunu kullanmak için lodash yüklemelisiniz (diğer cevaplardan anlaşılamamıştır):

$ npm install --save lodash

$ npm install --save @types/lodash

ve sonra ts dosyanıza aktarın:

import * as _ from "lodash";

7

Diğerlerinin daha önce de belirttiği gibi, lodash veya alt çizgi kullanmak muhtemelen en iyi çözümdür. Ancak bu kitaplıklara başka bir şey için ihtiyacınız yoksa, muhtemelen şuna benzer bir şey kullanabilirsiniz:

  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.

  }

Biz bunu yapmaya karar verdik.


1
// tarihlerin bağlantısını kaldırmak için ekleyebiliriz: if (Object.prototype.toString.call (obj) === '[object Date]') {return new Date (obj.getTime ()); }
A_J

1
veya örnek türünü kullanarak tarihi kontrol edin - if (obj instanceof Date) {return new Date (obj.getTime ())}
Anoop Isaac

0

Bu özelliğe sadece uygulama 'modellerini' oluşturmam gerekiyordu (nesnelere dönüştürülen ham arka uç verileri). Ben bir arada kullanarak sona erdi Yani Object.create ve (yeni belirlenen prototipten nesne oluşturmak) Object.assign (nesneler arasındaki özelliklerini kopyalamak). Derin kopyayı manuel olarak işlemeniz gerekiyor. Bunun için bir öz oluşturdum .


0

Aynı Soruna sahipti ve herhangi bir eklentiyi yalnızca derin klonlama için kullanmak istemiyordu:

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;
    }

Kredi: Bu işlevi daha okunaklı hale getirdim , lütfen aşağıdaki işlevsellik istisnalarını kontrol edin


0

Angular 5 veya üstü ile kullanmak için bir servis oluşturdum angular.copy (), angularjs tabanını kullanıyor, benim için iyi çalışıyor. Ek olarak isUndefined, vb. Gibi başka işlevler de vardır . Umarım yardımcı olur. Herhangi bir optimizasyon gibi, bilmek güzel olurdu. Saygılarımızla

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);
  }
}


0

Ben de sizin gibi angular.copy ve angular.expect gibi bir çalışma sorunu ile karşı karşıya kaldık çünkü onlar nesneyi kopyalamıyor veya bazı bağımlılıklar eklemeden nesneyi oluşturmuyorlar. Benim çözümüm şuydu:

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

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

JSON.stringify()Yöntem, bir JSON dizgeye bir JavaScript nesne veya değerine dönüştürür


2
Bu, onu tedavi etmenin en kötü yolu olduğu yukarıda ayrıntılı olarak söylendi!
Alessandro

0

Diziyi şu şekilde klonlayabilirsiniz:

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

Ve nesneyi şu şekilde klonlayın

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

0

Zaten lodash kullanmıyorsanız, sadece bu yöntem için kurmanızı tavsiye etmem. Bunun yerine 'klon' gibi daha dar kapsamlı bir kitaplık öneriyorum:

npm install clone
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.