我从api中检索了一个对象,将其保存在一个变量中,该变量可能不再被修改,但是被意外修改了
我检查了整个组件,所有功能以及html,以查找正在修改的位置,但没有成功。 我也已经更改了变量名称,以希望成为任何变量冲突,但也没有成功。 请记住,我之所以仅发布此问题,是因为我对该问题一无所知(它可能并且可能是,作为开发人员的我对javascript的一些无知),所以请客气
addArtigo(artigoInStock: Artigo) {
console.log('log 1:', this.assistenciaOpen.material[1].qty, this.material[1].qty); // log1: 2 2
const artigo = { ...artigoInStock, qty: 1 };
let materialQ = this.material;
if (artigoInStock.qty > 0) {
if (materialQ) {
if (materialQ.findIndex(item => item.id === artigo.id) < 0) {
materialQ = [...materialQ, artigo];
} else {
console.log('log 2:', this.assistenciaOpen.material[1].qty, this.material[1].qty); // log2: 2 2
materialQ.map(
itemQ => {
if (+itemQ.id === +artigo.id) {
itemQ.qty++;
return itemQ;
} else {
return itemQ;
}
}
);
console.log('log 3:', this.assistenciaOpen.material[1].qty, this.material[1].qty); // log3: 3 3
}
} else {
materialQ = [artigo];
}
const resultIndex = this.results.findIndex(result => result.id === artigo.id);
this.results[resultIndex].qty = this.results[resultIndex].qty - artigo.qty;
this.material = materialQ;
this.modal = false;
}
console.log('log 4:', this.assistenciaOpen.material[1].qty, this.material[1].qty); // log4: 3 3
// I was expecting log3 to be log3: 2 3 and log4:2 3
// for some reason, after materialQ.map() the this.assistenciaOpen.material[1].qty change, unexpectedly, from 2 to 3 (mirroring the this.material[1].qty)
}
}
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Router, ActivatedRoute, ParamMap } from '@angular/router';
import { map, concatMap, tap, toArray } from 'rxjs/operators';
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe';
import { Assistencia, Artigo } from 'src/app/shared/models';
import { PrintService } from 'src/app/pages/dashboard-page/prints/print.service';
import { UIService, UI } from 'src/app/shared/state/ui.service';
import { AssistenciasService, ArtigosService } from 'src/app/shared/state';
import { Observable, concat, of } from 'rxjs';
import { FormBuilder } from '@angular/forms';
@AutoUnsubscribe()
@Component({
selector: 'app-assistencia-page',
templateUrl: './assistencia-page.component.html',
styleUrls: ['./assistencia-page.component.scss']
})
export class AssistenciaPageComponent implements OnInit, OnDestroy {
public assistenciaOpen: Assistencia;
public modal = false;
public artigoSearchForm = this.fb.group({
input: [null]
});
public results: Artigo[];
public material: Partial<Artigo>[];
constructor(
private printService: PrintService,
private uiService: UIService,
private assistencias: AssistenciasService,
private artigos: ArtigosService,
private router: Router,
private route: ActivatedRoute,
private fb: FormBuilder) {
}
ngOnInit() {
this.route.paramMap
.pipe(
concatMap((params: ParamMap) => this.assistencias.getAndWatch(+params.get('id'))),
map((res: Assistencia[]) => res[0]),
concatMap(
assistencia => {
let assistMaterial: Partial<Artigo>[];
typeof assistencia.material === 'string'
? assistMaterial = JSON.parse(assistencia.material)
: assistMaterial = assistencia.material;
if (assistMaterial) {
return concat(
assistMaterial.map(
(artigo: Partial<Artigo>) => {
return this.artigos.get(artigo.id)
.pipe(
map((res: Artigo[]) => res[0]),
map(res => res = { ...res, qty: artigo.qty })
);
}
)
).pipe(
concatMap(concats => concats),
toArray(),
map( material => ({...assistencia, material})));
} else {
return of(assistencia);
}
}
),
tap( assistencia => this.assistenciaOpen = assistencia)
)
.subscribe(
(assistencia) => {
console.log(assistencia);
console.log('init');
this.material = assistencia.material;
}
);
}
ngOnDestroy() { }
saveChangesOnStock(material: Partial<Artigo>[], assistMaterial: Partial<Artigo>[]) {
if (material) {
console.log(material);
return concat(material.map(
(artigo: Artigo) => this.artigos.get(artigo.id)
.pipe(
map(res => res[0]),
concatMap(
(dbArtigo: Artigo) => {
const artigoToSave = { id: dbArtigo.id, qty: dbArtigo.qty - (artigo.qty) };
return this.artigos.patch(dbArtigo.id, artigoToSave);
}
)
)
)).pipe(concatMap(a => a), toArray());
} else {
console.log(null);
return of(null);
}
}
saveAssistencia(newEstado: string, assistencia: Assistencia) {
if (newEstado !== 'em análise' && !assistencia.relatorio_cliente) {
return alert('Preenche o relatório para o cliente!');
}
return this.saveChangesOnStock(this.material, null)
.pipe(
concatMap(
_ => this.assistencias.patch(assistencia.id, { ...assistencia, estado: newEstado, material: this.material })
.pipe(
tap(() => {
if (newEstado === 'entregue') { this.printService.printAssistenciaSaida(assistencia); }
}),
tap(() => window.history.back())
)
)
).subscribe();
}
openNewAssistenciaWithThisData(assistencia: Assistencia) {
this.uiService.patchState(
{
// modals
// pages
assistenciasCriarNovaPageContactoClienteForm: {
contacto: assistencia.cliente_user_contacto
},
assistenciasCriarNovaPageCriarNovaForm: {
...assistencia,
problema: `(Ficha anterior: ${assistencia.id}) `,
orcamento: null
},
// prints
}
)
.subscribe(() => this.router.navigate(['/dashboard/assistencias-criar-nova']));
}
navigateBack() {
window.history.back();
}
searchArtigo(input?: string) {
if (input) {
const inputSplited = input.split(' ');
const inputMapped = inputSplited.map(word =>
'{"$or": [' +
'{ "marca": { "$like": "%' + word + '%" }},' +
'{ "modelo": { "$like": "%' + word + '%" }},' +
'{ "descricao": { "$like": "%' + word + '%" }}' +
' ]}'
);
const dbQuery =
'{' +
'"query": {' +
'"$limit": "200",' +
'"$and": [' +
inputMapped +
']' +
'}' +
'}';
this.artigos
.findAndWatch(JSON.parse(dbQuery))
.pipe(
map((artigos: Artigo[]) => {
if (this.material) {
artigos.map(
artigo => {
const id = this.material.findIndex(item => item.id === artigo.id);
if (id > 0) {
console.log(
'id:' + id,
artigo.qty,
this.material[id].qty,
this.assistenciaOpen.material[id].qty,
artigo.qty - (this.material[id].qty - this.assistenciaOpen.material[id].qty));
artigo.qty = artigo.qty - (this.material[id].qty - this.assistenciaOpen.material[id].qty);
return artigo;
} else {
return artigo;
}
}
);
return artigos;
} else {
return artigos;
}
})
)
.subscribe((res: Artigo[]) => this.results = res);
}
}
addArtigo(artigoInStock: Artigo) {
console.log(this.assistenciaOpen.material[1].qty, this.material[1].qty);
const artigo = { ...artigoInStock, qty: 1 };
let materialQ = this.material;
if (artigoInStock.qty > 0) {
if (materialQ) {
if (materialQ.findIndex(item => item.id === artigo.id) < 0) {
materialQ = [...materialQ, artigo];
} else {
materialQ.map(
itemQ => {
if (+itemQ.id === +artigo.id) {
itemQ.qty++;
return itemQ;
} else {
return itemQ;
}
}
);
}
} else {
materialQ = [artigo];
}
const resultIndex = this.results.findIndex(result => result.id === artigo.id);
this.results[resultIndex].qty = this.results[resultIndex].qty - artigo.qty;
this.material = materialQ;
this.modal = false;
}
console.log(this.assistenciaOpen.material[1].qty, this.material[1].qty);
}
}
<div *ngIf="!assistenciaOpen">
<span class="spinner spinner-inline">
</span>
<span>
A carregar dados...
</span>
</div>
<div *ngIf="assistenciaOpen as assistencia">
<button type="button" class="btn btn-link btn-icon" style="margin:0; padding:0" (click)="navigateBack()">
<clr-icon class="is-solid" size="36" shape="arrow" style="transform: rotate(270deg);"></clr-icon>
</button>
<h3>Assistência {{assistencia.id}}</h3>
<div class="separate">
<div class="clr-row">
<div class="clr-col-md-2 fontheavy">
Estado:
</div>
<div class="clr-col">
{{assistencia.estado}}
</div>
</div>
</div>
<div class="separate">
<div class="clr-row">
<div class="clr-col-md-2 fontheavy">
Cliente:
</div>
<div class="clr-col">
{{assistencia.cliente_user_name}}
</div>
</div>
<div class="clr-row">
<div class="clr-col-md-2 fontheavy">
Técnico:
</div>
<div class="clr-col">
{{assistencia.registo_cronologico[assistencia.registo_cronologico.length-1].tecnico}}
</div>
</div>
<div class="clr-row">
<div class="clr-col-md-2 fontheavy">
Equipamento:
</div>
<div class="clr-col">
{{assistencia.categoria}}
{{assistencia.marca}}
{{assistencia.modelo}}
{{assistencia.cor}}
</div>
</div>
<div class="clr-row">
<div class="clr-col-md-2 fontheavy">
Serial:
</div>
<div class="clr-col">
{{assistencia.serial}}
</div>
</div>
<div class="clr-row">
<div class="clr-col-md-2 fontheavy">
Problema:
</div>
<div class="clr-col">
{{assistencia.problema}}
</div>
</div>
</div>
<div class="separate">
<div class="clr-row">
<div class="clr-col">
<div class="clr-row">
<div class="clr-col">
<button class="btn btn-link" (click)="modal = true">Adicionar material</button>
</div>
</div>
<div class="clr-row" *ngIf="material">
<div class="clr-col">
<table class="table">
<thead>
<tr>
<th class="left">Qty</th>
<th>Descrição</th>
<th>Local</th>
<th class="left">Custo p/ unidade</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let artigoY of material">
<td class="left">{{artigoY.qty}}</td>
<td>{{artigoY.descricao}} {{artigoY.marca}} {{artigoY.modelo}}</td>
<td>{{artigoY.localizacao}}</td>
<td class="left">{{artigoY.preco | currency: 'EUR'}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="separate">
<div class="clr-row">
<div class="clr-col-md-6">
<div class="clr-row">
<div class="clr-col fontheavy">
Informação técnica interna:
</div>
</div>
<div class="clr-row">
<div class="clr-col">
<!--
<textarea name="relatorio_interno" id="relatorio_interno" [attr.readonly]="assistencia.estado === 'contacto pendente'
|| assistencia.estado === 'não atendeu p/ cont.'
|| assistencia.estado === 'cliente adiou resp.'
|| assistencia.estado === 'orçamento pendente'
|| assistencia.estado === 'não atendeu p/ orç.'
|| assistencia.estado === 'cliente adiou orç.'
|| assistencia.estado === 'aguarda material'
|| assistencia.estado === 'concluído'
|| assistencia.estado === 'entregue'" [(ngModel)]='assistencia.relatorio_interno'></textarea>
-->
<textarea name="relatorio_interno" id="relatorio_interno"
[(ngModel)]='assistencia.relatorio_interno'></textarea>
</div>
</div>
</div>
<div class="clr-col-md-6">
<div class="clr-row">
<div class="clr-col fontheavy">
Informação para o cliente:
</div>
</div>
<div class="clr-row">
<div class="clr-col">
<textarea name="relatorio_cliente" id="relatorio_cliente"
[(ngModel)]='assistencia.relatorio_cliente'></textarea>
</div>
</div>
</div>
</div>
</div>
<div class="separate">
<div class="clr-row">
<div class="clr-col-md-2 fontheavy">
Orçamento:
</div>
<div class="clr-col">
<span *ngIf="assistencia.orcamento">€ {{assistencia.orcamento}}</span>
<span *ngIf="!assistencia.orcamento">não tem</span>
</div>
</div>
<div class="clr-row">
<div class="clr-col-md-2 fontheavy">
Preço:
</div>
<div class="clr-col-md-4">
€<input type="number" name="preco" id="preco" [(ngModel)]='assistencia.preco'>
</div>
</div>
</div>
<div class="separate">
<div *ngIf="assistencia.estado === 'recebido'
|| assistencia.estado === 'em análise'
|| assistencia.estado === 'contactado'
|| assistencia.estado === 'incontactável'
|| assistencia.estado === 'orçamento aprovado'
|| assistencia.estado === 'orçamento recusado'
|| assistencia.estado === 'material recebido'">
<button class="btn btn-primary btn-icon" (click)="saveAssistencia('em análise', assistencia)">
<clr-icon class="is-solid" shape="floppy" flip="vertical"></clr-icon>
Guardar
</button>
<clr-dropdown>
<button clrDropdownTrigger class="btn btn-outline-primary">+ Opções <clr-icon shape="caret down"></clr-icon>
</button>
<clr-dropdown-menu clrPosition="top-left" *clrIfOpen>
<button clrDropdownItem (click)="saveAssistencia('orçamento pendente', assistencia)">Orçamentar
</button>
<button clrDropdownItem (click)="saveAssistencia('contacto pendente', assistencia)">Contactar
</button>
<button clrDropdownItem (click)="saveAssistencia('concluído', assistencia)">Concluir</button>
</clr-dropdown-menu>
</clr-dropdown>
</div>
<div *ngIf="assistencia.estado === 'orçamento pendente'
|| assistencia.estado === 'não atendeu p/ orç.'
|| assistencia.estado === 'cliente adiou orç.'">
<button class="btn btn-success" (click)="saveAssistencia('orçamento aprovado', assistencia)">Aceite</button>
<clr-dropdown>
<button clrDropdownTrigger class="btn btn-outline-primary">+ Opções <clr-icon shape="caret down"></clr-icon>
</button>
<clr-dropdown-menu clrPosition="top-left" *clrIfOpen>
<button clrDropdownItem (click)="saveAssistencia('orçamento recusado', assistencia)">Recusado
</button>
<button clrDropdownItem (click)="saveAssistencia('cliente adiou orç.', assistencia)">Cliente adiou
resposta</button>
<button clrDropdownItem (click)="saveAssistencia('não atendeu p/ orç.', assistencia)">Não atendeu
</button>
<button clrDropdownItem (click)="saveAssistencia('incontactável', assistencia)">Incontactável
</button>
</clr-dropdown-menu>
</clr-dropdown>
</div>
<div *ngIf="assistencia.estado === 'contacto pendente'
|| assistencia.estado === 'não atendeu p/ cont.'
|| assistencia.estado === 'cliente adiou resp.'">
<button type="button" class="btn btn-success" (click)="saveAssistencia('contactado', assistencia)">
Sucesso
<clr-icon shape="caret down"></clr-icon>
</button>
<clr-dropdown>
<button clrDropdownTrigger class="btn btn-outline-primary">+ Opções <clr-icon shape="caret down"></clr-icon>
</button>
<clr-dropdown-menu clrPosition="top-left" *clrIfOpen>
<button type="button" (click)="saveAssistencia('contactado', assistencia)" clrDropdownItem>Cliente adiou
resposta</button>
<button type="button" (click)="saveAssistencia('não atendeu p/ cont.', assistencia)" clrDropdownItem>Não
atendeu</button>
<button type="button" (click)="saveAssistencia('incontactável', assistencia)"
clrDropdownItem>Incontactável</button>
</clr-dropdown-menu>
</clr-dropdown>
</div>
<div *ngIf="assistencia.estado === 'concluído'">
<button class="btn btn-warning" (click)="saveAssistencia('entregue', assistencia)">
Entregar
</button>
</div>
<div *ngIf="assistencia.estado === 'entregue'">
<button class="btn" (click)="openNewAssistenciaWithThisData(assistencia)">
Receber Novamente
</button>
</div>
</div>
</div>
<clr-modal [(clrModalOpen)]="modal" [clrModalSize]="'lg'">
<h3 class="modal-title">Adicionar Material (? Em construção...)</h3>
<div class="modal-body">
<form [formGroup]="artigoSearchForm" (ngSubmit)="searchArtigo(artigoSearchForm.value.input)">
<div class="clr-row">
<div class="clr-col-12 padding-bottom-05">
<input type="text" class="wd-100" placeholder="Digita algo..." name="input" formControlName="input" />
</div>
</div>
</form>
<h4>Resultados:</h4>
<div *ngIf="results as artigos">
<div *ngIf="artigos.length===0">? Não encontrei nada... Tenta novamente com atenção.</div>
<div *ngIf="artigos.length>0">
<r-data-row class="clr-row" *ngFor="let artigo of artigos; let listaIndex=index" (click)="addArtigo(artigo)">
<div class="clr-col-lg-3">
<span class="fontheavy">{{artigo.marca}}</span>
{{artigo.modelo}}
</div>
<div class="clr-col-lg-6 lessimportant">
{{artigo.descricao}}
</div>
<div class="clr-col-lg-1 lessimportant">
Qty: {{artigo.qty}}
</div>
<div class="clr-col-lg-2 lessimportant">
Local: {{artigo.localizacao}}
</div>
</r-data-row>
</div>
</div>
</div>
</clr-modal>
我希望this.assistenciaOpen仅被修改1次(当我通过ngOnInit()方法订阅api时
答案 0 :(得分:0)
我能想到的唯一原因是
点击(Assistencia => this.assistenciaOpen =助手)
您可以将其更改为
点击(Assistencia => {console.log(“获得一个值”); this.assistenciaOpen = Assistencia})
如果您看到这两个控制台转储之间有一个一个值,那么这就是原因。请在此处添加逻辑以处理此重新分配。
答案 1 :(得分:0)
好吧,我发现我的代码出了什么问题,我正在发布解决方案,以便以后可以为他人提供帮助。
我怀疑问题是由我对javascript如何复制对象的无知造成的。
尽管上面的函数有一些错误
(如:
->错误使用.map()
方法
->在.map()
内使用增量运算器
->加上由于其不纯正的行为,我根本不建议在任何地方使用++increment
和--decrement
算术运算符)
我完全不知道javascript是如何复制对象的。
在javascript中,当您从原始类型(Number
String
Boolean
undefined
和null
)复制值时,您正在创建值的副本转到新变量。
当您复制objects
(如object
,array
等)时,您实际上只是在新变量中创建了对旧变量的引用。
因此,当您更改变量的副本时,实际上是在更改原始变量。
const a = 4;
const b = a; // b = 4
++b;
console.log(a, b); // 4 5
const car = {
motor: {
fuel: 'gas',
intake: 'compressor'
},
color: red
};
const otherCar = car;
otherCar.motor.intake = 'turbo';
console.log(car.motor.intake); // turbo
console.log(otherCar.motor.intake); // turbo
为解决此问题,我找到了很多解决方案,从中我将指出2:
1-将库用作 immutablejs 来确保不变性(如果您使用typescript,则将很难实现,如果您想将immutable.Map
对象与类型一起使用,这是我们实现这一目标的主要原因全部使用打字稿)
2-需要复制时,使用...
传播算子复制 1级对象,或使用Ramda.clone复制深克隆 嵌套对象
您可以将库用作 Ramdajs 来制作嵌套对象的深层副本,但请明智地使用它,因为深层副本会对性能产生影响 >。
就我而言,我正在构建一个中等大小的webApp,但我什么也没注意到,但是了解这一点很重要。
您可以在freecodecamp.com上阅读this article,以了解有关javascript如何处理复制的更多信息