Apa alternatif untuk angular.copy di Angular

136

Bagaimana saya bisa menyalin objek dan kehilangan referensi di Angular?

Dengan AngularJS, saya bisa menggunakan angular.copy(object), tapi saya mendapatkan beberapa kesalahan saat menggunakannya di Angular.

PENGECUALIAN: ReferenceError: angulartidak didefinisikan

Rodrigo Real
sumber
Periksa solusi ini yang mungkin membantu: Tautan
Nourdine Alouane
Dalam banyak situasi, Anda mungkin ingin menggunakan .copy()tetapi sebenarnya tidak membutuhkannya. Dalam berbagai proyek AngJS1 yang saya lihat, itu adalah pembunuhan yang berlebihan, di mana salinan manual dari substruktur yang relevan akan dibuat untuk kode yang lebih bersih. Mungkin itu adalah bagian dari keputusan untuk tidak mengimplementasikannya oleh tim Angular.
phil294
omong-omong, terkait (dan juga tidak dijawab): stackoverflow.com/questions/41124528/…
phil294
Kemungkinan duplikat Apa cara paling efisien untuk
Michael Freidgeim

Jawaban:

180

Dengan asumsi Anda menggunakan ES6, Anda dapat menggunakan var copy = Object.assign({}, original). Bekerja di peramban modern; jika Anda perlu mendukung browser lama, periksa polyfill ini

memperbarui:

Dengan TypeScript 2.1+, notasi penyebaran objek steno ES6 tersedia:

const copy = { ...original }
Sasxa
sumber
75
Catatan yang angular.copy()membuat salinan yang dalam bertentangan dengan Object.assign(). Jika Anda ingin penyalinan dalam, gunakan lodash _.cloneDeep(value) lodash.com/docs#cloneDeep
bertrandg
di Webstorm saya dapatkan Unresolved function or method assign(); Rincian IDE: Webstorm 2016.2. Bagaimana saya bisa mengatasinya?
mihai
2
@meorfi Ke File -> Settings -> Languages & Frameworks -> Javascriptdan set Javascript language versionke ECMAScript 6.0.
Siri0S
@bertrandg _.clone (nilai) berbeda dari angular.copy (), itu tidak akan membuat contoh baru, sehingga _.cloneDeep (nilai) masih membuat referensi stackoverflow.com/questions/26411754/…
Zealitude
5
Selain itu, jika Anda menyalin array, gunakan:const copy = [ ...original ]
daleyjem
43

Hingga kami memiliki solusi yang lebih baik, Anda dapat menggunakan yang berikut:

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

EDIT: Klarifikasi

Harap dicatat: Solusi di atas hanya dimaksudkan untuk memperbaiki satu liner, disediakan pada saat Angular 2 sedang dalam pengembangan aktif. Harapan saya adalah kita akhirnya bisa mendapatkan yang setara angular.copy(). Karena itu saya tidak ingin menulis atau mengimpor perpustakaan kloning yang dalam.

Metode ini juga memiliki masalah dengan properti tanggal parsing (itu akan menjadi string).

Tolong jangan gunakan metode ini di aplikasi produksi . Gunakan hanya dalam proyek eksperimental Anda - yang Anda lakukan untuk mempelajari Angular 2.

Mani
sumber
11
ini merusak teman kencan Anda dan lambat sekali.
LanderV
5
Tidak selambat mengimpor seluruh perpustakaan untuk melakukan satu tugas, asalkan apa yang Anda lakukan cukup sederhana ...
Ian Belcher
1
ini mengerikan, tidak pernah menggunakannya
Murhaf Sousli
1
@MurhafSousli tolong coba untuk memahami konteks dari jawaban ini. Ini diberikan ketika Angular 2 sedang dalam pengembangan, dan harapannya adalah bahwa kita akhirnya akan mendapatkan setara dengan fungsi angular.copy (). Untuk menjembatani masa tunggu, saya menempatkan solusi ini sebagai opsi temp sampai kami memiliki solusi yang lebih baik. Ini adalah satu-baris dengan kloning yang dalam. Ini mengerikan , saya setuju ... Tetapi mengingat konteks eksperimental pada waktu itu, itu tidak terlalu buruk.
Mani
1
@ LazarLjubenović tentu saja pada tahun 2018 itu yang terjadi dan saya sangat setuju dengan Anda hari ini , tetapi pada tahun 2016 webpack tidak memiliki pohon yang bergetar sehingga Anda akan mengimpor seluruh perpustakaan dalam banyak kasus.
Ian Belcher
22

Alternatif untuk menyalin objek dalam yang memiliki objek bersarang di dalamnya adalah dengan menggunakan metode cloneDeep lodash.

Untuk Angular, Anda dapat melakukannya seperti ini:

Instal lodash dengan yarn add lodashatau npm install lodash.

Di komponen Anda, impor cloneDeepdan gunakan:

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

Ini hanya 18kb ditambahkan ke bangunan Anda, layak untuk manfaatnya.

Saya juga sudah menulis artikel di sini , jika Anda perlu wawasan lebih lanjut tentang mengapa menggunakan cloneDeep lodash.

BogdanC
sumber
2
"only 18kb" ditambahkan ke output hanya untuk bisa menyalin objek? JavaScript berantakan.
Endrju
Setelah membaca artikel yang direferensikan Anda, saya mengerti cloneDeepmetode ini instantiate objek baru. Haruskah kita masih menggunakannya jika kita sudah memiliki objek tujuan?
Stephane
17

Untuk menyalin dangkal Anda bisa menggunakan Object.assign yang merupakan fitur ES6

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

JANGAN menggunakannya untuk kloning yang dalam

mareks
sumber
3
Apa yang bisa digunakan untuk kloning dalam?
DB
15

Gunakan lodash sebagaimana ditunjukkan pertandg. Alasan angular tidak lagi memiliki metode ini, adalah karena sudut 1 adalah kerangka kerja yang berdiri sendiri, dan perpustakaan eksternal sering mengalami masalah dengan konteks eksekusi sudut. Angular 2 tidak memiliki masalah itu, jadi gunakan perpustakaan apa pun yang Anda inginkan.

https://lodash.com/docs#cloneDeep

LanderV
sumber
8

Jika Anda ingin menyalin instance kelas, Anda dapat menggunakan Object.assign juga, tetapi Anda harus memberikan instance baru sebagai parameter pertama (alih-alih {}):

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 !
Guillaume Nury
sumber
8

Solusi paling sederhana yang saya temukan adalah:

let yourDeepCopiedObject = _.cloneDeep(yourOriginalObject);

* LANGKAH PENTING: Anda harus menginstal lodash untuk menggunakan ini (yang tidak jelas dari jawaban lain):

$ npm install --save lodash

$ npm install --save @types/lodash

dan kemudian impor dalam file ts Anda:

import * as _ from "lodash";
William Hampshire
sumber
7

Seperti yang telah ditunjukkan orang lain, menggunakan lodash atau garis bawah mungkin adalah solusi terbaik. Tetapi jika Anda tidak membutuhkan perpustakaan itu untuk hal lain, Anda mungkin dapat menggunakan sesuatu seperti ini:

  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.

  }

Itulah yang kami putuskan untuk lakukan.

Pemarah
sumber
1
// untuk membatalkan tautan tanggal kita dapat menambahkan: if (Object.prototype.toString.call (obj) === '[objek Date]') {kembalikan Date baru (obj.getTime ()); }
A_J
1
atau periksa tanggal menggunakan tipe instance - if (obj instanceof Date) {return Date baru (obj.getTime ())}
Anoop Isaac
0

Saya membutuhkan fitur ini hanya dari 'model' aplikasi saya (data backend mentah dikonversi menjadi objek). Jadi saya akhirnya menggunakan kombinasi Object.create (membuat objek baru dari prototipe yang ditentukan) dan Object.assign (menyalin properti antar objek). Perlu menangani salinan dalam secara manual. Saya membuat intisari untuk ini.

mppfiles
sumber
0

Punya masalah yang sama, dan tidak ingin menggunakan plugin apa pun hanya untuk kloning mendalam:

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

Penghargaan: Saya membuat fungsi ini lebih mudah dibaca , silakan periksa pengecualian untuk fungsinya di bawah ini

Luc Bucher
sumber
0

Saya telah membuat layanan untuk digunakan dengan Angular 5 atau lebih tinggi, menggunakan angular.copy () basis angularjs, ini berfungsi dengan baik untuk saya. Selain itu ada fungsi lain seperti isUndefined, dll. Saya harap ini membantu. Seperti pengoptimalan apa pun, akan menyenangkan untuk diketahui. salam

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

César Holmes
sumber
0

Saya dan Anda juga menghadapi masalah pekerjaan angular.copy dan angular.expect karena mereka tidak menyalin objek atau membuat objek tanpa menambahkan beberapa dependensi. Solusi saya adalah ini:

  copyFactory = (() ->
    resource = ->
      resource.__super__.constructor.apply this, arguments
      return
    this.extendTo resource
    resource
  ).call(factory)
Vitya Ivliiev
sumber
0
let newObj = JSON.parse(JSON.stringify(obj))

The JSON.stringify()Metode mengkonversi objek JavaScript atau nilai ke string JSON

CharithJ
sumber
2
Ini telah dikatakan secara mendalam di atas bahwa ini adalah cara terburuk untuk mengobatinya!
Alessandro
0

Anda dapat mengkloning seperti Array

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

Dan tirukan objek seperti

this.assignCustomer = Object.assign({}, this.customer);
Padmanabhan Velu
sumber
0

Jika Anda belum menggunakan lodash, saya tidak akan merekomendasikan menginstalnya hanya untuk metode yang satu ini. Saya sarankan sebagai gantinya perpustakaan khusus yang lebih sempit seperti 'klon':

npm install clone
Ruben Sivan
sumber