Angular 10 - Chained Promises on Array Don't Calculate All Properties - arrays

I'm new to Angular and TypeScript, and have encountered a problem:
I have an array of objects of type LabObject model: my LabObject model has quite a lot of properties, along with two private properties that are calculated (private _labTest: number and private _vitalSign: number).
I create an array of type LabObject and populate it using a for loop. Naturally, I have noticed that these two calculations don't finish while the loop is still running, because they are quite heavy, so I figured I'd use a promise.
I thought if I ran the loop in the first promise, and then chained two promises after that, one for each calculation, it would force my calculations to finish running before I did anything else with that array.
It seems that I am wrong, as not all array elements wind up with calculated _labTest and _vitalSign, and in some elements either one or both of them are missing.
Here is my method:
createFile() {
let getLabObject = new Promise((resolve, reject) => {
let lab_objects: LabObject[] = [];
for (let i = 0; i < 10; i++) {
let lo = this.createLabObject();
lab_objects.push(lo);
}
resolve(lab_objects);
});
let getLabTest = objects => {
return new Promise((resolve, reject) => {
objects.forEach(item => {
item.CalculateLabTest();
});
resolve(objects);
});
};
let getVitalSign = objects => {
return new Promise((resolve, reject) => {
objects.forEach(item => {
item.CalculateVitalSign();
});
resolve(objects);
});
};
let backend = objects => {
this.http.post('my backend url address', JSON.stringify(objects))
.subscribe(responseData => {
console.log(responseData);
});
}
getLabObject.then(objects => {
return getLabTest(objects);
}).then(objects => {
return getVitalSign(objects);
}).then(objects => {
return backend(objects);
});
}
I get a JSON object that looks something like this:
[{id: 1, name: 'name1'},
{id: 2, name: 'name2', _labTest: 10},
{id: 3, name: 'name3', _vitalSign: 17},
{id: 4, name: 'name4', _labTest: 8, _vitalSign: 6}]
But I would like for the _labTest and _vitalSign to be calculated for each and every one of the elements.
What am I doing wrong?

I don't think you need promises for this. Actually the asynchronous code is probably the cause of the incomplete objects.
What you are looking for is a getter function. This lets you access a method that calculate a value as-if it is a property. So it is always correct and easy to access. Add a function to extract an object from your LabObject and you are ready to submit it to your backend.
Check this StackBlitz
app.component.ts:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
labtest: LabObject[] = [];
ngOnInit() {
this.labtest.push( new LabObject( 2, 8 ) );
this.labtest.push( new LabObject( 2, 5 ) );
this.labtest.push( new LabObject( 34, 1 ) );
this.labtest.push( new LabObject( 10, 1 ) );
}
log() {
const json = this.labtest.map( labtest => labtest.as_object() );
console.log( JSON.stringify(json) );
// instead of logging you want to submit the JSON to your backend to do whatever you wanted to do...
}
}
class LabObject {
constructor(
public type_a_test: number,
public type_b_test: number
) {}
private get _labTests(): number {
return this.type_a_test + this.type_b_test;
}
private get _vitalSign(): number {
return 2;
}
public as_object(): object {
return {
labtests: this._labTests,
vitalsigns: this._vitalSign
}
}
}

Related

ANGULAR Components array key in result get value by id

this.crudService.get('user.php?mode=test')
.subscribe((data:any) => {
{ for (var key in data) { this[key] = data[key]; } };
}
);
This use to work on angular 7 now on angular 13 i get this error (look image)
In template i was using the values for example in json string was and array and i had users, in template was {{users}} , {{posts}} etc.. now the this[key] give error , please help me out its very important can't find solution
i'll show an example code, and then applied to your code:
Example
// creating global variables to receive the values
users: any = null;
posts: any = null;
// simulating the data you will receive
data: any[] = [
{users: ['user1', 'user2', 'user3']},
{posts: ['post1', 'post2', 'post3']}
];
getCrudService() {
// access each object of the array
this.data.forEach(obj => {
// getting keys name and doing something with it
Object.keys(obj).forEach(key => {
// accessing global variable and setting array value by key name
this[String(key)] = obj[String(key)]
})
})
}
Apllied to your code
this.crudService.get('user.php?mode=test').subscribe((data:any) => {
data.forEach(obj => {
Object.keys(obj).forEach(key => {
this[String(key)] = obj[String(key)]
});
});
});
I hope it helped you, if you need help, just reply me.

String to array (object) Angular TS 12

I have a CSV file (local), converted it to a string, part of the string is like:
44,"3845657"
51,"3847489"
1,"3888510"
79,"3840471"
57,"3864492"
After I receive input number (first value), I want to match it to the second value (string).
so if input is 51, I want to be able to return 3847489.
No headers in the csv.
CSV to string:
fetchData() {
fetch('../../../assets/static/mapping.csv')
.then(response => response.text())
.then(data => {
// Do something with your data
console.log(data);
this.mappingCSV = data;
});
}
outputs:
44,"3845657"
51,"3847489"
1,"3888510"
79,"3840471"
57,"3864492"
Other ways to convert a csv to an array of objects is also welcome, not married with my csv to string method.
I'm using HTTPClient in this example which is a built-in service class available in Angular. Here's how to use HTTPClient of Angular for you to read and know its benefits.
In my .ts file, I split the text-converted csv first for any newline. Then I added a loop in which I split the text by comma and pushed the necessary details to the new csvArray.
export class SampleComponent {
public csvArr: CsvArray[] = [];
constructor(private http: HttpClient) {
this.http.get('assets/csv.csv', {
responseType: 'text'
}).subscribe(
(data) => {
const csvToRowArray = data.split('\n');
console.log(csvToRowArray);
for (let index = 0; index < csvToRowArray.length; index++) {
const row = csvToRowArray[index].split(',');
this.csvArr.push(new CsvArray(parseInt(row[0], 10), row[1]));
}
console.log(this.csvArr);
},
(error) => {
console.log(error);
}
);
}
}
export class CsvArray {
id: number;
text: string;
constructor(id: number, text: string) {
this.id = id;
this.text = text;
}
}
I created a stackblitz so that you can check my implementation.

Angular decrement value in array

I try to decrement a value in my array, but I can't get it to work.
My array data contains attributes and everytime a method gets clicked, I call that value from a service and increment it in the array object. The getter is equal to amountCounter.
My main problem is that whenever I try to remove an array object, my amountCounter won't also decrement the value which it had before, but the array object gets removed.
I also put two pictures to better clarify my problem, thank you so much for every help.
app.component.html
<h2>Add values of my service into array:</h2>
<p>Array:</p>
<p>Total: {{amountCounter}}</p>
<div *ngFor="let item of data, let i = index;">
<span>ID: {{item.id}}</span>
<span>Title: {{item.title}}</span>
<span (click)="removeElement(i, item.amountCounter)" class="material-icons">
close
</span>
</div>
app.component.ts
export class AppComponent {
clickEventsubscription: Subscription
ngOnInit() {
}
id: number;
title: String;
amountCounter: number;
data: any = [];
constructor(private share: ShareDataService) {
this.clickEventsubscription = this.share.getClickEvent().subscribe(() => {
this.initialize();
})
}
removeElement(id: number, counter: number) {
this.data.splice(id, 1);
this.amountCounter -= counter //In that line I can't get it to work that my attribute decrements
console.log("before" + this.amountCounter);
console.log("after:" + counter);
}
initialize() {
this.id = this.share.getId();
this.title = this.share.getTitle();
this.amountCounter = this.share.getAmountCounter();
const newData = {
id: this.id,
title: this.title,
amountCounter: this.amountCounter
};
this.data.push(newData);
console.log(this.data);
}
}
share-data.service.ts
export class ShareDataService {
private subject = new Subject<any>();
title: String;
id: number;
amountCounter: number;
getId() {
return this.id;
}
getTitle() {
return this.title;
}
getAmountCounter(){
return this.amountCounter;
}
sendClickEvent() {
this.subject.next();
}
getClickEvent(): Observable<any> {
return this.subject.asObservable();
}
}
That is how my array looks before ID 1 is clicked
That is how my array looks after I clicked at "X", but it decrements wrong
Thank you so much!
Not sure if this is the behavior you are after but generally this method will calculate the sum of the array values
getTotalAmount(): number {
return this.data.reduce((acc, item) => acc + item.amount, 0);
}
The main issue I found very difficult to figure out is that you have amountCounter in [share-data.service, dialog.component, app.component]
I suppose you want to add new items using dialog.component with different amount values.
Here you add new item to your 'data' array, the values for single item comes from share service which was updated in your dialog.component
initialize() {
console.log("initialize");
const id = this.share.getId();
const title = this.share.getTitle();
const amount = this.share.getAmount();
const newData = {
id,
title,
amount
};
this.data.push(newData);
}
To summarize the flow:
in dialog.component you update field values in share-data.service clickMe() method
that method will trigger a method in app.component called initialize which will add the new item to the this.data array.
if you click on item (to remove it) splice will do it, and Angular will refresh the Total calling the getTotalAmount method
Working Stackblitz.

How to convert 3 arrays into a single Object in TypeScript

I am working on an Angular project and I am fetching data from this API
Then I do this:
export class ListComponent implements OnInit {
readonly API_URL = 'https://corona.lmao.ninja/countries';
country = [];
cases = [];
deaths = [];
recovered = [];
countriesData: Object;
constructor(private httpClient: HttpClient) { }
ngOnInit() {
this.httpClient.get(this.API_URL).subscribe((data: Array<any>) => {
data = data.filter(c => c.country !== 'World');
data.forEach(y => {
englishToGreek(this.country, y.country);
this.cases.push(y.cases);
this.deaths.push(y.deaths);
this.recovered.push(y.recovered);
});
console.table(this.countriesData);
});
}
}
The englishToGreek() function translates country names from English to Greek, the source code is this:
const lexor = new Map();
lexor.set('World', 'Παγκοσμίος'); // in extreme cases
lexor.set('USA', 'Η.Π.Α / Αμερική');
lexor.set('Spain', 'Ισπανία');
lexor.set('Italy', 'Ιταλία');
lexor.set('France', 'Γαλλία');
lexor.set('Iran', 'Ιράν');
lexor.set('Germany', 'Γερμανία');
lexor.set('UK', 'Ηνωμένο Βασίλειο');
lexor.set('Turkey', 'Τουρκία');
lexor.set('Switzerland', 'Ελβετία');
lexor.set('Netherlands', 'Ολλανδία');
lexor.set('Canada', 'Καναδάς');
lexor.set('Belgium', 'Βέλγιο');
function englishToGreek(pushableObject, countryName) {
pushableObject.push(lexor.get(countryName));
}
How can I combine the 3 arrays this.cases, this.deaths, this.recovered and assign the result to the this.countriesData object?
Just import the lexor map into same component and use the following code to accomplish your task.
data.forEach(y => {
this.country.push({
country: lexor.get(y.country),
cases: y.cases,
deaths: y.deaths,
recovered: y.recovered,
})
});

angular2 observables filter each array element than return it to an array

I have an array of Threads Objects with ID, title and a isBookmarked:boolean.
I've created a Subject and I want to subscribe to it in order to get an array of Thread Objects with isBookmarked=true.
https://plnkr.co/edit/IFGiM8KoSYW6G0kjGDdY?p=preview
Inside the Service I have
export class Service {
threadlist:Thread[] = [
new Thread(1,'Thread 1',false),
new Thread(2,'Thread 2',true),
new Thread(3,'Thread 3',false),
new Thread(4,'Thread 4',true),
new Thread(5,'Thread 5',true),
new Thread(6,'Thread 6',false),
new Thread(7,'Thread 7',false),
]
threadlist$:Subject<Thread[]> = new Subject<Thread[]>()
update() {
this.threadlist$.next(this.threadlist)
}
}
in the component
export class AppComponent implements OnInit {
localThreadlist:Thread[];
localThreadlistFiltered:Thread[];
constructor(private _service:Service){}
ngOnInit():any{
//This updates the view with the full list
this._service.threadlist$.subscribe( threadlist => {
this.localThreadlist = threadlist;
})
//here only if isBookmarked = true
this._service.threadlist$
.from(threadlist)//????
.filter(thread => thread.isBookmarked == true)
.toArray()
.subscribe( threadlist => {
this.localThreadlistFiltered = threadlist;
})
}
update() {
this._service.update();
}
}
which Instance Method do I use in general to split an array?
Also is there a better way to do it?
Thanks
You would leverage the filter method of JavaScript array within the map operator of observables:
this._service.threadlist$
.map((threads) => {
return threads.filter((thead) => thread.isBookmarked);
})
.subscribe( threadlist => {
this.localThreadlistFiltered = threadlist;
});
See this plunkr: https://plnkr.co/edit/COaal3rLHnLJX4QmvkqC?p=preview.

Resources