Was ist die Alternative zu angle.copy in Angular?

134

Wie kann ich ein Objekt kopieren und seine Referenz in Angular verlieren?

Mit AngularJS kann ich verwenden angular.copy(object), aber ich erhalte einen Fehler, wenn ich das in Angular verwende.

AUSNAHME: ReferenceError: angularist nicht definiert

Rodrigo Real
quelle
Überprüfen Sie diese Lösung, es könnte helfen: Link
Nourdine Alouane
In vielen Situationen möchten Sie es möglicherweise verwenden .copy(), benötigen es jedoch nicht. In verschiedenen AngJS1-Projekten, die ich gesehen habe, war es ein Overkill, bei dem eine manuelle Kopie der relevanten Unterstrukturen für einen saubereren Code gesorgt hätte. Vielleicht war das Teil der Entscheidung, es vom Angular-Team nicht umzusetzen.
Phil294
Übrigens
phil294

Antworten:

179

Angenommen, Sie verwenden ES6, können Sie verwenden var copy = Object.assign({}, original). Funktioniert in modernen Browsern; Wenn Sie ältere Browser unterstützen müssen, sehen Sie sich diese Polyfüllung an

aktualisieren:

Mit TypeScript 2.1+ ist die ES6-Kurzschreibobjekt-Notationsnotation verfügbar:

const copy = { ...original }
Sasxa
quelle
75
Beachten Sie, dass im angular.copy()Gegensatz zu eine tiefe Kopie erstellt wird Object.assign(). Wenn Sie eine tiefe Kopie wünschen, verwenden Sie lodash _.cloneDeep(value) lodash.com/docs#cloneDeep
bertrandg
in Webstorm bekam ich Unresolved function or method assign(); IDE-Details: Webstorm 2016.2. Wie kann ich das lösen?
Mihai
2
@meorfi Gehe zu File -> Settings -> Languages & Frameworks -> Javascriptund setze Javascript language versionauf ECMAScript 6.0.
Siri0S
@bertrandg _.clone (Wert) unterscheidet sich von angle.copy (). Es wird keine neue Instanz erstellt. Als _.cloneDeep (Wert) wird weiterhin ein Referenzstack erstellt. overflow.com/questions/26411754/…
Zealitude
5
Wenn Sie ein Array kopieren, verwenden Sie außerdem:const copy = [ ...original ]
Daleyjem
43

Bis wir eine bessere Lösung haben, können Sie Folgendes verwenden:

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

EDIT: Klarstellung

Bitte beachten Sie: Die obige Lösung war nur als Schnellreparatur-Einzeiler gedacht, der zu einem Zeitpunkt bereitgestellt wurde, als Angular 2 in der aktiven Entwicklung war. Meine Hoffnung war, dass wir irgendwann ein Äquivalent von bekommen könnten angular.copy(). Daher wollte ich keine Deep-Cloning-Bibliothek schreiben oder importieren.

Diese Methode hat auch Probleme mit dem Parsen von Datumseigenschaften (es wird eine Zeichenfolge).

Bitte verwenden Sie diese Methode nicht in Produktions-Apps . Verwenden Sie es nur in Ihren experimentellen Projekten - denjenigen, die Sie zum Erlernen von Angular 2 ausführen.

Mani
quelle
11
Das ruiniert deine Daten und ist höllisch langsam.
LanderV
5
Nicht so langsam wie das Importieren einer ganzen Bibliothek, um eine einzelne Aufgabe zu erledigen, solange das, was Sie tun, ziemlich einfach ist ...
Ian Belcher
1
das ist schrecklich, benutze das nie
Murhaf Sousli
1
@ MurhafSousli bitte versuchen Sie den Kontext dieser Antwort zu verstehen. Dies wurde bereitgestellt, als Angular 2 in der Entwicklung war, und die Hoffnung war, dass wir irgendwann ein Äquivalent der Funktion angle.copy () erhalten. Um die Wartezeit zu überbrücken, habe ich diese Lösung als temporäre Option angeboten, bis wir eine bessere Lösung haben. Dies ist ein Einzeiler mit tiefem Klonen. Das ist schrecklich , stimme ich zu ... Aber angesichts des experimentellen Kontextes zu dieser Zeit ist es nicht so schlimm.
Mani
1
@ LazarLjubenović natürlich im Jahr 2018 ist das der Fall und ich stimme Ihnen heute voll und ganz zu , aber im Jahr 2016 hatte das Webpack kein Baumschütteln, so dass Sie in den meisten Fällen eine ganze Bibliothek importieren würden.
Ian Belcher
22

Die Alternative zum tiefen Kopieren von Objekten mit verschachtelten Objekten besteht in der Verwendung der cloneDeep-Methode von lodash.

Für Angular können Sie dies folgendermaßen tun:

Installieren Sie lodash mit yarn add lodashoder npm install lodash.

Importieren cloneDeepund verwenden Sie es in Ihrer Komponente :

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

Es sind nur 18 KB zu Ihrem Build hinzugefügt, was sich für die Vorteile lohnt.

Ich habe hier auch einen Artikel geschrieben , wenn Sie mehr darüber erfahren möchten, warum Sie lodashs cloneDeep verwenden.

BogdanC
quelle
2
"Nur 18 KB" zur Ausgabe hinzugefügt, um nur Objekte tief kopieren zu können? JavaScript ist ein Chaos.
Endrju
Nachdem ich Ihren Artikel gelesen habe, verstehe ich, dass die cloneDeepMethode ein neues Objekt instanziiert. Sollten wir es trotzdem verwenden, wenn wir bereits ein Zielobjekt haben?
Stephane
17

Zum flachen Kopieren können Sie Object.assign verwenden, eine ES6-Funktion

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

Verwenden Sie es NICHT zum tiefen Klonen

Mareks
quelle
3
Was kann zum tiefen Klonen verwendet werden?
DB
15

Verwenden Sie lodash wie angegeben. Der Grund, warum Angular diese Methode nicht mehr hat, ist, dass Angular 1 ein eigenständiges Framework war und externe Bibliotheken häufig auf Probleme mit dem Winkelausführungskontext stießen. Angular 2 hat dieses Problem nicht. Verwenden Sie daher die gewünschte Bibliothek.

https://lodash.com/docs#cloneDeep

LanderV
quelle
8

Wenn Sie eine Klasseninstanz kopieren möchten, können Sie auch Object.assign verwenden, müssen jedoch eine neue Instanz als ersten Parameter übergeben (anstelle von {}):

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
quelle
8

Die einfachste Lösung, die ich gefunden habe, ist:

let yourDeepCopiedObject = _.cloneDeep(yourOriginalObject);

* WICHTIGE SCHRITTE : Sie müssen lodash installieren, um dies zu verwenden (was aus anderen Antworten nicht ersichtlich war):

$ npm install --save lodash

$ npm install --save @types/lodash

und importieren Sie es dann in Ihre ts-Datei:

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

Wie andere bereits betont haben, ist die Verwendung von Lodash oder Unterstrich wahrscheinlich die beste Lösung. Wenn Sie diese Bibliotheken jedoch für nichts anderes benötigen, können Sie wahrscheinlich Folgendes verwenden:

  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.

  }

Dafür haben wir uns entschieden.

Spitfire
quelle
1
// Um ​​die Verknüpfung von Daten aufzuheben, können wir hinzufügen: if (Object.prototype.toString.call (obj) === '[Objektdatum]') {neues Datum zurückgeben (obj.getTime ()); }
A_J
1
oder überprüfen Sie das Datum mit dem Instanztyp - if (obj instanceof Date) {neues Datum zurückgeben (obj.getTime ())}
Anoop Isaac
0

Ich brauchte diese Funktion nur aus meinen App-Modellen (in Objekte konvertierte Backend-Rohdaten). Daher habe ich eine Kombination aus Object.create (neues Objekt aus angegebenem Prototyp erstellen) und Object.assign ( Kopiereigenschaften zwischen Objekten) verwendet. Die tiefe Kopie muss manuell bearbeitet werden. Ich habe einen Kern dafür erstellt.

mppfiles
quelle
0

Hatte das gleiche Problem und wollte keine Plugins nur zum tiefen Klonen verwenden:

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: Ich habe diese Funktion besser lesbar gemacht . Bitte überprüfen Sie die Ausnahmen zu ihrer Funktionalität unten

Luc Bucher
quelle
0

Ich habe einen Dienst für Angular 5 oder höher erstellt. Er verwendet die angular.copy ()Basis von AngularJS und funktioniert gut für mich. Zusätzlich gibt es andere Funktionen wie isUndefinedusw. Ich hoffe es hilft. Wie bei jeder Optimierung wäre es schön zu wissen. Grüße

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
quelle
0

Ich und Sie hatten ein Problem mit der Arbeit angular.copy und angular.expect, da sie das Objekt nicht kopieren oder erstellen, ohne einige Abhängigkeiten hinzuzufügen. Meine Lösung war folgende:

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

Die JSON.stringify()Methode konvertiert ein JavaScript-Objekt oder einen JavaScript-Wert in eine JSON-Zeichenfolge

CharithJ
quelle
2
Dies wurde bereits ausführlich gesagt, dass dies der schlechteste Weg ist, es zu behandeln!
Alessandro
0

Sie können das Array wie folgt klonen

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

Und klonen Sie das Objekt wie

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

Wenn Sie lodash noch nicht verwenden, würde ich nicht empfehlen, es nur für diese eine Methode zu installieren. Ich schlage stattdessen eine eng spezialisierte Bibliothek wie "Klon" vor:

npm install clone
Reuben Sivan
quelle