This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 4 years ago.
I have component in my angular 5 project, bill-page. On bill-page I get some info, bill and currency. It is two arrays, bill (array with one object, get it from my firebase) and currency (array with two objects, get it from free api). When I wanna chek this arrays by using
console.log()
it shows me that is two normal arrays with some objects.
export class BillPageComponent implements OnInit {
currency: any = [];
bill: any = [];
userId: string;
isLoaded = false;
copyCurrency: any = [];
constructor(private billService: BillService, private authService: AuthService) {}
ngOnInit() {
this.isLoaded = false;
this.userId = this.authService.returnAuthState();
this.billService.getBillFirebase(this.userId).subscribe(
(resp)=>{
for (let key in resp) {this.bill.push(resp[key])}
}
);
console.log(this.bill);//[{currency: "USD", value: 0}]
this.billService.getCurrencyPB().subscribe((data)=>{
this.currency.push(data[0]);
this.currency.push(data[1]);
});
console.log(this.currency); //[{key1:1, key2:2},{key3:3, key4:4}]
this.isLoaded = true;
}
But when I try get this objects from arrays, or property from objects it becomes undefined, for example
console.log(this.bill[0])// undefined or console.log(this.bill[0].value)//undefined
or if I try copy this arrays it becomes undefined too
this.copyCurrency = this.currency.slice();
console.log(this.copyCurrency) // undefined
Basically what happened was that it executed aysnchronously. console.log (this.bill) executed before it got any value. this.bill gets the value only inside the subscribe function.
Related
I have created one service to load all the files data:
readonly file= new BehaviorSubject(null);
readonly file$ = this.pnlNumbers.asObservable();
getFile(filename: string) {
this.file.next(null);
this.subscriptions.push(this.http.get(`/file/${filename}).subscribe(data => {
this.file.next(data);
}, error => {
this.file.next(error);
}));
}
This will return an single object with file information ,eg:
{
id:0001,
name: 'test_file.txt',
...
}
I have created ab function to store all the result data that comes from the getFile service:
getAllFiles(): any {
let filesList= [];
this.activeFilesList.forEach(fileName => {
this.fileService.getFile(fileName);
});
this.fileService.file$.subscribe((data) => {
if (data) {
fileList?.push(data);
}
});
return filesList;
}
I don't know why , but "typeOf this.getAllFiles()" will return an Object instead of Array, for that reason I cant access the indices of filesList[], eg on chrome console:
[]
1:{id:0001,name:'test.file.text'}
2:{id:0002,name:'test.file2.text'}
3:{id:0003,name:'test.file3.text'}
the filesList.lenght = 0
I need that this filesList[] be a type of Array instead of an Object.
Few things here:
Firstly, a common JavaScript gotcha is the return type of an array is in fact 'object'
typeof [] // 'object'
typeof ['any array contents'] // 'object'
i.e. typeof is an ineffective heuristic for determining whether the return type of this function is an array. Use Array.isArray instead:
Array.isArray(['any array contents']) // true
Array.isArray({ someKey: 'someValue' }) // false
Secondly, am I safe to assume that this line
readonly file$ = this.pnlNumbers.asObservable();
Should instead be
readonly file$ = this.file.asObservable();
otherwise the rest of the code does not really have much relevance, as this.pnlNumbers is not referenced anywhere else
Thirdly, it appears that you are trying to combine the results of multiple asynchronous streams (in this case http calls) into a single array to be returned from getAllFiles(). As these are asynchronous, they by nature take some time to return their data. While this data is being returned, the rest of your synchronous function code will run, and that means your return statement will be hit before the http calls have returned their data.
In its current state, getAllFiles() is simply returning the value of filesList before any of the http calls have returned their values, i.e. the default value it was assigned, [].
What you will need to do instead is to
Use an RxJs operator to combine those independent http streams into one stream
Subscribe to this combined stream and handle the combination of values as is appropriate for the operator being used
An example implementation using forkJoin is here, but depending on your use case, other joining operators like concat, mergeMap, combineLatest etc may be preferable:
type FileType = { id: number; name: string }
getAllFiles$(): Observable<FileType[]> {
const filesListObservables: Observable<FileType>[] =
this.activeFilesList
.map(
(fileName: string) => this.fileService.getFile(fileName)
);
const filesList$: Observable<FileType[]> = forkJoin(filesListObservables);
return filesList$;
}
getAllFiles(): void {
this.getAllFiles$()
.subscribe(
(allFiles: FileType[]) => {
console.log(allFiles) // [{id:0001,name:'test.file.text'},...]
}
)
}
This question already has answers here:
Angular unit test combineLatest
(2 answers)
Closed last year.
Im having a problem writing unit tests for observable in Angular... Im trying to test cases if displayPaymentsLineItem$ will be true or false depending on the values of
mobileAccountBalance$, and selectedMobileDigitalBill$... Can anyone help?
public selectedMobileDigitalBill$: Observable<MobileDigitalBill>;
public mobileAccountBalance$: Observable<MobileAccountBalance>;
private expandedLinesMap: object = {};
private expandedTaxesAndFees: boolean = false;
private devices: MobileDevice[] = [];
private destroy$: Subject<void> = new Subject();
constructor(]
private mobileStatementsTabFacade: MobileStatementsTabFacade,
private billingMobileFacade: BillingMobileFacade,
) {}
ngOnInit() {
this.mobileAccountBalance$ = this.mobileStatementsTabFacade.mobileAccountBalance$;
this.displayPaymentsLineItem$ = combineLatest([
this.mobileAccountBalance$,
this.selectedMobileDigitalBill$,
]).pipe(
map(([mobileAccountBalance, selectedMobileDigitalBill]: [MobileAccountBalance, MobileDigitalBill]) => {
const isPastDue: boolean = mobileAccountBalance?.pastdue > 0;
const hasPayments: boolean = selectedMobileDigitalBill?.payments?.length > 0;
return isPastDue && hasPayments;
})
);
}
});
You can take(1) (take one value, then complete) and subscribe to test if the emitted value is falsy. Observables need to be completed if you test them this way.
describe('The display payment line items observable', () => {
it('should emit truthy', () => {
displayPaymentsLineItem$
.pipe(take(1))
.subscribe(value =>{
expect(value).toBeTruthy();
});
}
}
That being said, displayPaymentsLineItem$ won't emit anything if the two observables inside combineLatest() aren't defined in your test. Since they come from two facades, they may need to be provided before starting your test.
Also, about your code example:
displayPaymentsLineItem$ isn't declared before the constructor.
selectedMobileDigitalBill$ is declared but is never defined before it is referenced inside combineLatest().
This question already has answers here:
Console.log showing only the updated version of the object printed
(3 answers)
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
I'm retrieving data from api and storing it in a sharedState. When i'm trying to access this data like below it seems to return the correct. There is one object in the dashboard array with name field.
console.log(newState.profile)
returns:
However when i access dashboards
console.log(newState.profile.dashboards)
it returns
[]
function to set the state from main.js
export function getProfile(state, user) {
const newState = Object.assign({}, state);
if (user) {
db().collection("users").doc(user.uid)
.onSnapshot((doc) => {
var data = doc.data()
if (data) {
newState.profile.apps = data.apps
newState.profile.dashboards = data.dashboards
} else {
authservice.setUserAcc(user.uid)
}
});
}
return newState
}
i'm not sure wether this is a javascript behaviour or related to the way i did set up the state.
This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 4 years ago.
export class myclass implements OnInit {
private outagesList;
private outageArray;
constructor(private outages: OutagesService){}
ngOnInit(){
this.outages.getOutagesList().subscribe((data) => {
this.outagesList = data;
this.outagesList.forEach( function (arrayItem)
{
this.outageArray.push(arrayItem["title"]);
});
)
}
If i try to push the data into outageArray it is throwing ERROR TypeError: Cannot read property 'outageArray' of undefined
How can i avoid this error? I need to access this outageArray value to html.
Use an arrow function
this.outagesList.forEach((arrayItem) => {
this.outageArray.push(arrayItem["title"]);
});
Once you are using post-ES6 Javascript, functions like forEach are not likely to be your best solution. The easiest way to do this is with a for..of loop:
this.outages.getOutagesList().subscribe((data) => {
this.outagesList = data;
for (let arrayItem of this.outagesList)
{
this.outageArray.push(arrayItem.title);
});
)
This question already has answers here:
What is the use of the JavaScript 'bind' method?
(23 answers)
Closed 4 years ago.
I have some question about function named bind.
I am testing some public source codes implemented in Angular.
I am trying to understand what the bind function, I searched internet but can't find proper answer. Could you give some guide for this?
export class AppComponent {
currentPage: number = 1;
news: Array<any> = [];
scrollCallback;
constructor(private hackerNewsSerivce: HackerNewsService) {
this.scrollCallback = this.getStories.bind(this);
}
getStories() {
return this.hackerNewsSerivce.getLatestStories(this.currentPage).do(this.processData);
}
private processData = (news) => {
this.currentPage++;
this.news = this.news.concat(news.json());
}
}
In terms of angular, function binding is used to declare a function in component/directive and define in the scope / this.
And we use where we need like volatile