Jak mogę skopiować obiekt i stracić jego odniesienie w Angular?
Z AngularJS mogę używać angular.copy(object)
, ale otrzymuję błąd podczas używania tego w Angular.
WYJĄTEK: ReferenceError:
angular
nie jest zdefiniowany
Jak mogę skopiować obiekt i stracić jego odniesienie w Angular?
Z AngularJS mogę używać angular.copy(object)
, ale otrzymuję błąd podczas używania tego w Angular.
WYJĄTEK: ReferenceError:
angular
nie jest zdefiniowany
.copy()
ale w rzeczywistości nie potrzebujesz go. W różnych projektach AngJS1, które widziałem, była to przesada, w której ręczna kopia odpowiednich podstruktur byłaby zrobiona dla czystszego kodu. Może to była część decyzji o nie wdrożeniu go przez zespół Angular.
Odpowiedzi:
Zakładając, że używasz ES6, możesz użyć var copy = Object.assign({}, original)
. Działa w nowoczesnych przeglądarkach; jeśli potrzebujesz obsługi starszych przeglądarek, sprawdź ten polyfill
aktualizacja:
W przypadku języka TypeScript 2.1+ dostępna jest skrócona notacja rozprzestrzeniania obiektów w ES6:
const copy = { ...original }
angular.copy()
tworzy głęboką kopię w przeciwieństwie do Object.assign()
. Jeśli chcesz głęboką kopię, użyj lodash _.cloneDeep(value)
lodash.com/docs#cloneDeep
Unresolved function or method assign()
; Szczegóły IDE: Webstorm 2016.2. Jak mogę to rozwiązać?
File -> Settings -> Languages & Frameworks -> Javascript
i ustaw Javascript language version
na ECMAScript 6.0
.
const copy = [ ...original ]
Dopóki nie mamy lepszego rozwiązania, możesz skorzystać z:
duplicateObject = <YourObjType> JSON.parse(JSON.stringify(originalObject));
EDYCJA: wyjaśnienie
Uwaga: powyższe rozwiązanie miało być tylko jednym szybkim rozwiązaniem, dostarczonym w czasie, gdy Angular 2 był aktywnie rozwijany. Miałem nadzieję, że w końcu uda nam się uzyskać odpowiednik angular.copy()
. Dlatego nie chciałem pisać ani importować biblioteki do głębokiego klonowania.
Ta metoda ma również problemy z analizowaniem właściwości daty (stanie się ciągiem znaków).
Nie używaj tej metody w aplikacjach produkcyjnych . Używaj go tylko w swoich eksperymentalnych projektach - tych, które robisz do nauki Angulara 2.
Alternatywą dla głębokiego kopiowania obiektów z zagnieżdżonymi obiektami jest użycie metody cloneDeep lodash.
W przypadku Angulara możesz to zrobić w następujący sposób:
Zainstaluj lodash za pomocą yarn add lodash
lub npm install lodash
.
W swoim komponencie zaimportuj go cloneDeep
i użyj:
import { cloneDeep } from "lodash";
...
clonedObject = cloneDeep(originalObject);
Do kompilacji zostało dodane tylko 18 KB, co jest warte korzyści.
Napisałem tu również artykuł , jeśli potrzebujesz więcej informacji na temat tego, dlaczego używasz cloneDeep lodash.
cloneDeep
metoda tworzy instancję nowego obiektu. Czy nadal powinniśmy go używać, jeśli mamy już obiekt docelowy?
Użyj lodash zgodnie ze wskazaniami bertandg. Powodem, dla którego angular nie ma już tej metody, jest to, że angular 1 był samodzielnym szkieletem, a biblioteki zewnętrzne często napotykały problemy z kontekstem wykonania kątowego. Angular 2 nie ma tego problemu, więc użyj dowolnej biblioteki.
Jeśli chcesz skopiować instancję klasy, możesz również użyć Object. assign, ale musisz przekazać nową instancję jako pierwszy parametr (zamiast {}):
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 !
Najprostsze rozwiązanie, które znalazłem, to:
let yourDeepCopiedObject = _.cloneDeep(yourOriginalObject);
* WAŻNE KROKI: Musisz zainstalować lodash, aby użyć tego (co było niejasne z innych odpowiedzi):
$ npm install --save lodash
$ npm install --save @types/lodash
a następnie zaimportuj go do swojego pliku ts:
import * as _ from "lodash";
Jak już inni zauważyli, użycie lodash lub podkreślenia jest prawdopodobnie najlepszym rozwiązaniem. Ale jeśli nie potrzebujesz tych bibliotek do niczego innego, prawdopodobnie możesz użyć czegoś takiego:
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.
}
To właśnie postanowiliśmy zrobić.
Potrzebowałem tej funkcji po prostu z „modeli” mojej aplikacji (surowe dane zaplecza przekonwertowane na obiekty). Więc skończyło się za pomocą kombinacji Object.create (tworzenie nowego obiektu z określonego prototypu) oraz Object.assign (Kopiowanie właściwości między obiektami). Musisz ręcznie obsługiwać głęboką kopię. Stworzyłem do tego sedno .
Miałem ten sam problem i nie chciałem używać żadnych wtyczek tylko do głębokiego klonowania:
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;
}
Kredyty: sprawiłem, że ta funkcja jest bardziej czytelna , sprawdź poniżej wyjątki od jej funkcjonalności
Ja, jak ty, stanęliśmy przed problemem pracy kątowej. Kopie i kątowe. Oczekujemy, ponieważ nie kopiują obiektu ani nie tworzą obiektu bez dodawania pewnych zależności. Moje rozwiązanie było takie:
copyFactory = (() ->
resource = ->
resource.__super__.constructor.apply this, arguments
return
this.extendTo resource
resource
).call(factory)
let newObj = JSON.parse(JSON.stringify(obj))
JSON.stringify()
Metoda konwertuje obiekt JavaScript lub wartość na ciąg JSON
Możesz sklonować Array jak
this.assignCustomerList = Object.assign([], this.customerList);
I sklonuj obiekt jak
this.assignCustomer = Object.assign({}, this.customer);
Jeśli jeszcze nie używasz lodash, nie polecałbym instalowania go tylko dla tej jednej metody. Proponuję zamiast tego bardziej wyspecjalizowaną bibliotekę, taką jak „clone”:
npm install clone
Stworzyłem usługę do użytku z Angular 5 lub nowszym, korzysta angular.copy()
z bazy angularjs, działa dobrze dla mnie. Dodatkowo są inne funkcje, takie jak isUndefined
itp. Mam nadzieję, że to pomoże. Jak w przypadku każdej optymalizacji, dobrze byłoby wiedzieć. pozdrowienia
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);
}
}