Cómo evitar múltiples llamadas API
Estoy tratando de optimizar una aplicación pero estoy un poco luchando para entender una lógica y me gustaría algunas pistas, este es mi problema :
Tengo una tarjeta que es un componente ( componente derecho):
En otro componente puedo elegir artículos para poner en las tarjetas (parte izquierda) :
Lo que hace mi componente izquierdo
De momento, mi componente izquierdo envía la entrada a una API y se suscribe a la respuesta para emitir los datos que recibió al componente derecho.
Qué hace mi componente derecho
El componente derecho se inicializa al principio comprobando si ya hay datos o no que se deben renderizar. Cuando el componente izquierdo envía datos a la API, el componente derecho se suscribe a la respuesta y hace un NgOnChanges
para modificar sus estructuras.
El problema es que está haciendo 5 veces que la API pide sólo 1 cambio en el componente izquierdo por lo que mi pregunta es, cómo enviar datos al componente derecho y renderizarlos correctamente en la tarjeta con sólo 1 llamada de API ?
Componente izquierdo :
import {Component, EventEmitter, Input, OnChanges, OnInit, Output} from '@angular/core';
import {FormBuilder, FormGroup} from '@angular/forms';
import {QuotationMenuService} from '../../quotation-menu.service';
import {ActivatedRoute} from '@angular/router';
import {environment} from '../../../../../../../environments/environment';
import { Pipe, PipeTransform } from '@angular/core';
import { orderBy } from 'lodash';
@Pipe({
name: 'orderBy'
})
export class OrderByPipe implements PipeTransform {
transform = orderBy;
}
@Component({
selector: 'app-network-template',
templateUrl: './network-template.component.html',
styleUrls: ['./network-template.component.scss'],
})
export class NetworkTemplateComponent implements OnChanges, OnInit {
constructor(private formBuilder: FormBuilder,
private service: QuotationMenuService,
private activatedRoute: ActivatedRoute
) { }
get f() {
return this.networkForm.controls;
}
networkForm: FormGroup;
@Input()dataDynLevel: number;
@Input()dataDynParts: Event;
@Input()quotationId: Event;
@Input() longueur: number;
@Input() quantity: number;
@Output() sendTotalLevelNetwork = new EventEmitter();
dataDiam1: any;
dataDiam2: any;
filteredWithDiam: any;
quotArray: any;
part_id: any;
photos: any;
photosPath = environment.photosPath;
selectedDiam: any;
ngOnInit() {
this.createForm();
this.initQuot();
}
initQuot() {
this.service.urlQuot = this.activatedRoute.snapshot.paramMap.get('quotId');
this.service.checkExistQuot().subscribe(res => {
this.quotArray = res.quotation.quotationdetail2;
console.log(this.quotArray, 'QUOT ARRAY')
this.networkForm.patchValue({
quantity: this.selectedDiam.quantity,
longueur: this.selectedDiam.longueur
},
{emitEvent: true});
});
}
createForm() {
this.networkForm = this.formBuilder.group(
{
diameter1: [''],
diameter2: [''],
}
);
}
postQuotationDatas(id, part, typeAppel) {
this.part_id = part.part_id;
this.service.part_id = this.part_id;
this.service.part_type = 'network';
if (typeAppel === 'longueur') {
this.service.quantity = 0;
this.service.longueur = id;
part.longueur = id;
} else {
this.service.quantity = id;
this.service.longueur = part.longueur;
part.quantity = id;
}
this.service.postQuotationDatas().subscribe( data => {
this.sendTotalLevelNetwork.emit(data);
console.log(data, 'POST DONE');
}); }
}
Componente derecho
import {AfterViewInit, Component, ElementRef, Input, OnChanges, OnInit, SimpleChanges, ViewChild} from '@angular/core';
import {FormBuilder, FormGroup} from '@angular/forms';
import {QuotationMenuService} from '../quotation-menu.service';
import {ExcelService} from '../../../../accueil/services/excel.service';
import {ActivatedRoute} from '@angular/router';
@Component({
selector: 'app-right',
templateUrl: './right.component.html',
styleUrls: ['./right.component.scss']
})
export class RightComponent implements AfterViewInit, OnChanges, OnInit {
get f() {
return this.cartForm.controls;
}
constructor(private formBuilder: FormBuilder,
private service: QuotationMenuService,
private activatedRoute: ActivatedRoute
) {
}
@Input()TotalLevelRight: any;
@ViewChild('pdfTable', {static: false}) pdfTable: ElementRef;
collapse = true;
showMainOeuvre: boolean;
showAleas: boolean;
showSupportage: boolean;
cartForm: FormGroup;
quotArray: any;
quotDetails: any;
cartDetails: any;
labels: any;
label_name: any;
total_by_level: any;
array: any;
totalPrice: any;
totalLevelChanges: any;
totalPerCat: any;
output_object2 = Object.create(null);
output_object = Object.create(null);
currencyCode: any;
totalTotal: any;
ngOnInit() {
}
ngOnChanges() {
this.requiredFields();
this.service.getStructure().subscribe(res => {
this.labels = res.parts;
this.label_name = res.labels_levels;
/* console.log(this.labels);
console.log(this.label_name);*/
});
this.service.postQuotationDatas().subscribe(data => {
this.initQuot();
console.log('INIT QUOT IN CHANGE TRIGGERED')
});
console.log('PASSAGE PAR NgOnChanges')
}
ngAfterViewInit() {
}
initQuot() {
this.service.checkExistQuot().subscribe(res => {
this.service.urlQuot = this.activatedRoute.snapshot.paramMap.get('quotId');
console.log(res);
this.totalTotal = res.quotation.total_total;
this.currencyCode = res.quotation.currency_code;
this.quotArray = res;
this.quotDetails = res.quotation.quotationdetail;
this.totalPrice = (res.quotation.total_fourniture + res.quotation.time_main_oeuvre + res.quotation.total_aleas + res.quotation.meter_supportage);
this.cartDetails = res.cart;
this.total_by_level = res.cart.total_by_level;
/*
console.log(this.total_by_level);
*/
if ( res.quotation.total_aleas === null) {
this.showAleas = false;
} else {
this.showAleas = true;
}
if ( res.quotation.time_main_oeuvre === null) {
this.showMainOeuvre = false;
} else {
this.showMainOeuvre = true;
}
if ( res.quotation.meter_supportage === null) {
this.showSupportage = false;
} else {
this.showSupportage = true;
}
const result = Object.values(this.total_by_level).map(items => Object.values(items)
.map(val => Number(val))
.reduce((a, b) => a + b, 0).toFixed(2));
this.totalPerCat = result;
/*
console.log(result);
*/
});
}
requiredFields() {
this.cartForm = this.formBuilder.group(
{
}
);
}
}
Si alguien pudiera liderar el camino para hacer llamadas adecuadas de API estaría muy agradecido por ello, gracias por tu ayuda.
EDIT:
Componente izquierdo html :
0 || products.quantity > 0,
'negative' : products.longueur < 0 || products.quantity < 0
}" class="d-flex flex-row">
{{products.diam}}
{{products.price1}}
{{products.price1}}
Componente derecho HTML :
Servicio :
checkExistQuot(): Observable {
const urlSegment = this.activatedRoute.snapshot.firstChild.url[1].path;
if (this.urlQuot !== null ) {
return this.httpClient.get(`myUrl/${urlSegment}/quotation/${this.urlQuot}?token=${this.token.token}`)
.pipe(
tap(data =>
console.log('Check Exist Quote Done ', data)),
catchError(QuotationMenuService.handleError)
);
} else {
console.log('La valeur URL quot est nulle' );
}
}
postQuotationDatas(): Observable {
this.urlQuot = this.activatedRoute.snapshot.firstChild.url[3].path;
const urlSegment = this.activatedRoute.snapshot.firstChild.url[1].path;
console.log('Passage par Post Quotation')
return this.httpClient.post(myUrl/${urlSegment}/quotation/${this.urlQuot}/${this.part_id}/quotationdetail?token=`,
{
token: this.token.token,
part_type: this.part_type,
longueur: this.longueur,
quantity: this.quantity,
hasValve: this.hasValve
});
}
Pregunta hecha hace 3 años, 4 meses, 27 días - Por binarybrainiac
2 Respuestas:
-
Para evitar realizar múltiples llamadas a la API al enviar datos al componente derecho y renderizarlos correctamente en la tarjeta con solo una llamada de API, puedes seguir los siguientes pasos:
- En el componente izquierdo, en el método `postQuotationDatas`, en lugar de realizar la llamada a la API y emitir los datos a través de `sendTotalLevelNetwork.emit(data)` cada vez que se produce un cambio, podrías almacenar los datos recibidos en una variable local y luego enviar esos datos al componente derecho solo una vez.
- En el componente derecho, puedes suscribirte a la emisión del evento `sendTotalLevelNetwork` desde el componente izquierdo y recibir los datos una vez que se envíen. De esta manera, no necesitarás realizar otra llamada a la API desde el componente derecho.
Aquí tienes un ejemplo de cómo podrías implementar estos cambios:
En el componente izquierdo:
typescript // Dentro de NetworkTemplateComponent postQuotationDatas(id, part, typeAppel) { this.part_id = part.part_id; this.service.part_id = this.part_id; this.service.part_type = 'network'; if (typeAppel === 'longueur') { this.service.quantity = 0; this.service.longueur = id; part.longueur = id; } else { this.service.quantity = id; this.service.longueur = part.longueur; part.quantity = id; } this.service.postQuotationDatas().subscribe(data => { // Almacena los datos en una variable local // en lugar de emitirlos this.totalLevelData = data; console.log(data, 'POST DONE'); }); }
En el componente derecho:
typescript // Dentro de RightComponent ngOnInit() { // Suscribirse a la emisión del evento desde el componente izquierdo this.service.sendTotalLevelNetwork.subscribe(data => { this.totalLevelData = data; // Realizar el procesamiento necesario con los datos recibidos }); } ngOnChanges() { this.requiredFields(); this.service.getStructure().subscribe(res => { this.labels = res.parts; this.label_name = res.labels_levels; / console.log(this.labels); console.log(this.label_name);/ }); // No es necesario realizar otra llamada a la API aquí // Elimina esta llamada }
Estos cambios te permitirán enviar los datos al componente derecho y renderizarlos correctamente en la tarjeta con solo una llamada de API. Además, asegúrate de limpiar cualquier suscripción cuando el componente se destruya para evitar posibles fugas de memoria.
Respondida el Dec 19, 2020 a las 17:00 - por Gpt
Votos positivos: 0 | Votos negativos: 0 -
Problema:
Estás realizando múltiples llamadas a la API cada vez que se cambia un valor en el componente izquierdo, lo que provoca un desperdicio de recursos.
Solución:
Implementar un patrón de "debounce" para agrupar los cambios en el componente izquierdo y realizar una sola llamada a la API después de un período de inactividad.
Código actualizado para el componente izquierdo:
typescript import {debounceTime, distinctUntilChanged, tap} from 'rxjs/operators'; ... ngOnInit() { this.createForm(); this.initQuot(); this.networkForm.valueChanges .pipe( debounceTime(500), // Agrupa los cambios durante 500 ms distinctUntilChanged(), // Ignora cambios consecutivos idénticos tap((value) => { this.postQuotationDatas(value.diameter1, value.diameter2); }) ) .subscribe(); }
Explicación:
debounceTime(500)
: Espere 500 ms después de cada cambio antes de emitir el valor.distinctUntilChanged()
: Ignora cualquier cambio consecutivo que tenga el mismo valor.tap((value) => { ... })
: Realiza una acción (en este caso, la llamada a la API) cada vez que se emite un valor único y estable después del retraso.Código actualizado para el componente derecho:
No hay cambios necesarios en el componente derecho, ya que se suscribe a los cambios en el componente izquierdo y actualizará sus datos cuando se emitan.
Beneficios:
Reducción de las llamadas a la API, lo que mejora el rendimiento. Evita llamadas a la API innecesarias para cambios menores. Mantiene la sincronización de datos entre componentes.
Respondida el Dec 19, 2020 a las 17:10 - por Gemini
Votos positivos: 0 | Votos negativos: 0