I am new to Angular and i am facing some difficulties with a task. I have an array of IDs that i want to execute the same GET Call over. And for every GET call result i have to do some operations and then add the result of every operation to some arrays. I managed to find a way to do it correctly. But my problem is, i can't manage to wait for the final result to be ready (after all the GET calls are done and the operations too) before giving it as an argument to another method that will send it with a POST call.
the method where i do the GET calls and the operations over every call's result (the problem occurs when i am in the rollBackSPN condition).
async getComponentIds(taskName: String, selectedComponents: IComponent[]) {
const componentsId: number[] = [];
const componentsWithoutParams: IComponent[] = [];
let sendPortaPrecedente : boolean;
if(taskName == "rollBackSPN"){
from(selectedComponents).pipe(
concatMap(component =>{
return this.http.get<any>("Url"+component.idComponent).pipe(
tap(val => {
sendPortaPrecedente = true;
for(const obj of val){
if((obj.name == "z0bpqPrevious" && obj.value == null) || (obj.name == "datePortaPrevious" && obj.value == null) || (obj.name == "typePortaPrevious" && obj.value == null)){
sendPortaPrecedente = false;
}
}
if(sendPortaPrecedente){
componentsId.push(component.idComponent);
}else{
componentsWithoutParams.push(component);
}
}),
catchError(err => {
return of(err);
})
)
})
).subscribe(val => {
return { componentsId : componentsId, componentsWithoutParams : componentsWithoutParams, sendPortaPrecedente : sendPortaPrecedente};
});
}else{
for (const component of selectedComponents) {
componentsId.push(component.idComponent)
return { componentsId : componentsId, componentsWithoutParams : componentsWithoutParams, sendPortaPrecedente : sendPortaPrecedente};
}
}
}
The method where i pass the getComponentIds(taskName: String, selectedComponents: IComponent[]) result so it can be send with a POST call (again when i am in the rollBackSPN condition)
executeTask(serviceIdSi: string, actionIdSi: string, actionClassName: string, componentName: string, taskName: string,
componentsId: number[], componentsWithoutParams: IComponent[], sendPortaPrecedente: boolean): Observable<any> {
const url = this.taskUrl + `?serviceId=${serviceIdSi}` + `&actionId=${actionIdSi}` + `&actionClassName=${actionClassName}`
+ `&componentName=${componentName}` + `&taskName=${taskName}`;
if(taskName == "rollBackSPN"){
if(sendPortaPrecedente && componentsWithoutParams.length == 0){
return this.http.post<any>(url, componentsId);
}else{
let errMessage = "Some Error Message"
for(const component of componentsWithoutParams){
errMessage = errMessage + component.idComponent +"\n";
}
throw throwError(errMessage);
}
}else{
return this.http.post<any>(url, componentsId);
}
}
Both these methods are defined in a service called TaskService. And the service is called like this in a component UnitTaskButtonsComponent.
async launchUnitTask() {
this.isLoading = true;
this.isClosed = false;
this.appComponent.currentComponentIndex = this.componentIndex;
let res = await this.taskService.getComponentIds(this.unitTaskLabel, this.selectedComponents);
this.taskService.executeTask(this.appComponent.currentService.identifiantSi,
this.appComponent.currentAction.identifiantSi,
this.appComponent.currentAction.className,
this.selectedComponents[0].name,
this.unitTaskLabel,
res.componentsId,
res.componentsWithoutParams,
res.sendPortaPrecedente).subscribe(
data => this.executeTaskSuccess(),
error => this.executeTaskError());
}
"res" properties are always undefined when it's a rollBackSPN task.
The main issue here is that getComponentIds
does not return a Promise
. So await
ing does not work. I would suggest to change getComponentIds
so that it returns an Observable
instead.
getComponentIds(taskName: string, selectedComponents: IComponent[]) {
// ^^^^^^ use string instead of String
return forkJoin(
selectedComponents.map((component) => {
return this.http.get<any>("Url" + component.idComponent).pipe(
map((val) => {
let sendPortaPrecedente = true;
for (const obj of val) {
if (
(obj.name == "z0bpqPrevious" && obj.value == null) ||
(obj.name == "datePortaPrevious" && obj.value == null) ||
(obj.name == "typePortaPrevious" && obj.value == null)
) {
sendPortaPrecedente = false;
}
}
return { component, sendPortaPrecedente }
}),
catchError((err) => of(err))
);
})
).pipe(
map((result) => {
const componentsId: number[] = [];
const componentsWithoutParams: IComponent[] = [];
for (const val of result) {
if (val.sendPortaPrecedente) {
componentsId.push(val.component.idComponent);
} else {
componentsWithoutParams.push(val.component);
}
}
return { componentsId, componentsWithoutParams };
})
);
}
Instead of using concatMap
, let's use a forkJoin
. The forkJoin
allows sending all requests in parallel and returns the result in an array. But we have to pass in an array of Observable
s. That's why we map over the selectedComponents
.
In the lower map
, we can now get the complete result of the http calls in the result
parameter. Here we do the processing of the data. I was not really sure how to handle the sendPortaPrecedente
. You will have to fill that in.
We simply return the whole Observable
async launchUnitTask() {
this.taskService
.getComponentIds(this.unitTaskLabel, this.selectedComponents)
.pipe(
switchMap((res) => {
this.taskService
.executeTask(
this.appComponent.currentService.identifiantSi,
this.appComponent.currentAction.identifiantSi,
this.appComponent.currentAction.className,
this.selectedComponents[0].name,
this.unitTaskLabel,
res.componentsId,
res.componentsWithoutParams,
res.sendPortaPrecedente
)
})
).subscribe(
(data) => this.executeTaskSuccess(),
(error) => this.executeTaskError()
);
}
In the launchUnitTask
method, we don't use await
anymore. Instead, we call getComponentIds
and chain the call of executeTask
with a switchMap
.