How to get first value and then another subscribe method - javascript

I have developed a simple angular 7 web app. firebase database connectivity,
I am trying to store the first list in an array using the subscribe method and then console.log that array.
but before that data get the array will print undefined after some time it will get data.
How can code wait for the response is done and then print that array.
import { Injectable } from '#angular/core';
import { AngularFireList, AngularFireDatabase } from 'angularfire2/database';
#Injectable({
providedIn: 'root'
})
export class DressesService {
constructor(public firebase: AngularFireDatabase) { }
getJoinDresses(){
return this.firebase.list('makavana-tailor/dresses').snapshotChanges()
}
}
import { Component, OnInit } from '#angular/core';
import { DressesService } from '../../services/dresses/dresses.service';
import { map } from 'rxjs/operators';
import { Observable } from 'rxjs';
#Component({
selector: 'app-con-dress',
templateUrl: './con-dress.component.html',
styleUrls: ['./con-dress.component.css']
})
export class ConDressComponent implements OnInit {
constructor(private dresses: DressesService) { }
dressArray = [];
ngOnInit() {
this.getAllDresses();
console.log(this.dressArray)
}
getAllDresses(){
this.dresses.getJoinDresses().subscribe(actions => {
this.dressArray = actions.map(action => {
return {
$key: action.key,
...action.payload.val()
}
})
})
}
}

Your question title is not clear. But if I understand your problem correctly, you are facing an issue in working with asynchronous calls. Either you have to print console.log(this.dressArray) inside the subscribe or return the observable data from getAllDresses and subscribe to it within onInit()
code :
ngOnInit() {
this.getAllDresses().subscribe(data => {
this.dressArray = data;
console.log(this.dressArray)
});
}
getAllDresses(){
return this.dresses.getJoinDresses().pipe(map(actions => {
return actions.map(action => {
return {
$key: action.key,
...action.payload.val()
}
})
}))
}

The problem with your current code is that you show the array before it has a chance to be populated.
You know it's populated when the subscribe function is called.
So the easiest is to modify your code by moving the console.log inside the subscribe call:
ngOnInit() {
this.getAllDresses();
}
getAllDresses(){
this.dresses.getJoinDresses().subscribe(actions => {
this.dressArray = actions.map(action => ({
$key: action.key,
...action.payload.val()
}));
console.log(this.dressArray);
})
}

Related

Changes in one Array is affecting other Array in Angular project

I am creating a angular project where i am getting API data which i am displaying to user. It has two components "Navigation" and "NewsBlock" and a service called "newsFetchService". API data is fetched by newsfetch service and used by both components.
NewsFetchService
import { Injectable } from '#angular/core';
import { Observable, of } from 'rxjs';
import { NewsData } from './NewsData';
import { HttpClient,HttpHeaders } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class NewsfetchserviceService {
//Global variable storing All fetch data
newsBlockData : NewsData[] = [];
allNews : NewsData[] = [];
constructor(
private http:HttpClient
) { }
private newsFetchURL = 'api/v1/topics';
getNews() {
return new Promise((resolve, reject) => {
this.http.get<NewsData[]>(this.newsFetchURL).subscribe(res => {
this.allNews = res;
this.newsBlockData = res;
resolve(true);
})
})
}
updateNewsBlock(selectedNews : NewsData){
this.newsBlockData.length = 0;
this.newsBlockData.push(selectedNews);
}
}
navigation.component.ts
import { Component, OnInit } from '#angular/core';
import { BreakpointObserver, Breakpoints } from '#angular/cdk/layout';
import { Observable } from 'rxjs';
import { map, shareReplay } from 'rxjs/operators';
import { NewsfetchserviceService } from '../newsfetchservice.service';
import { NewsData } from '../NewsData';
#Component({
selector: 'app-navigation',
templateUrl: './navigation.component.html',
styleUrls: ['./navigation.component.css']
})
export class NavigationComponent implements OnInit{
sourcesList : NewsData[] = [];
ngOnInit(): void {
this.showAllSources();
}
isHandset$: Observable<boolean> = this.breakpointObserver.observe(Breakpoints.Handset)
.pipe(
map(result => result.matches),
shareReplay()
);
constructor(private breakpointObserver: BreakpointObserver,private newsfetch: NewsfetchserviceService) {}
showAllSources():void {
this.sourcesList = this.newsfetch.allNews;
/* this.newsfetch.getNews().subscribe(news => this.news = news); */
}
updateNewsList(source : NewsData):void{
console.log('option selected');
console.log(source);
this.newsfetch.updateNewsBlock(source);
}
}
newsBlock.component.ts
import { Component, OnInit } from '#angular/core';
import { NewsData } from '../NewsData';
import { NewsfetchserviceService } from '../newsfetchservice.service';
#Component({
selector: 'app-newsblock',
templateUrl: './newsblock.component.html',
styleUrls: ['./newsblock.component.css']
})
export class NewsblockComponent implements OnInit {
constructor(private newsfetch: NewsfetchserviceService) { }
newsBlockData : NewsData[] = [];
ngOnInit(): void {
this.getNews();
}
getNews():void {
this.newsBlockData = this.newsfetch.newsBlockData;
/* this.newsfetch.getNews().subscribe(news => this.news = news); */
}
}
Now, when user click a field in Navigation component it updates the newsBlockData array in Newsfetchservice. This newsBlockData is used by "newsBlock" component which is correctly updating data based on data changed.
Issue i am facing is that updating data within newsBlockData array is also affecting allnews array Data. Any data added or removed from newsBlockData array is also reflected in allnews array even though they are two separate arrays.
I have tried changing approach like trying to use subscriber and promise but getting the same issue. Also tried deep copying and shallow copying but getting the same result
The problem is in the getNews() method. Even though you initialize both properties to point to a separate array:
newsBlockData : NewsData[] = [];
allNews : NewsData[] = [];
In your getNews() method, you point them to the same array:
getNews() {
return new Promise((resolve, reject) => {
this.http.get<NewsData[]>(this.newsFetchURL).subscribe(res => {
this.allNews = res;
this.newsBlockData = res;
resolve(true);
})
})
}
A shallow copy in this case should be sufficient:
getNews() {
return new Promise((resolve, reject) => {
this.http.get<NewsData[]>(this.newsFetchURL).subscribe(res => {
this.allNews = [...res];
this.newsBlockData = [...res];
resolve(true);
})
})
}

Can't get deeper into the response data object in subscribe's callback function. Why?

I'm fetching data from RandomUser api with Angular HttpClient. I've created a method in a service calling GET, mapping and returning a Observable. Then I subscribe on this method in a component importing this service and in subscribe's callback I am trying to store the response data in a local variable. The problem is I can't get "deeper" into this response object than:
this.randomUser.getNew().subscribe(data => {
this.userData = data[0];
})
If I'm trying to reach any further element of that response object, and log it to console it I get "undefined". To be precise I cant reference to, for example:
this.randomUser.getNew().subscribe(data => {
this.userData = data[0].name.first;
})
If I store the "data[0]" in a variable first I can get into these unreachable properties. What is the reason of it? Please, help. Let me know what important piece of fundamental JS (or Angular) knowledge I'm not aware of. As far as I know I should be able to do what I am trying to do :)
service looks like these
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { map } from 'rxjs/operators';
import { Observable } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class RandomUserService {
url: string = " https://randomuser.me/api/ "
constructor(private http: HttpClient) { }
public getNew(): Observable<any> {
return this.http.get(this.url)
.pipe(map(responseData => {
const returnDataArray = [];
for (const key in responseData) {
returnDataArray.push(responseData[key])
}
return returnDataArray;
}))
}
}
component looks like these:
import { Component, OnInit } from '#angular/core';
import { RandomUserService } from 'src/app/shared/random-user.service';
import { Observable } from 'rxjs';
#Component({
selector: 'app-single-character',
templateUrl: './single-character.component.html',
styleUrls: ['./single-character.component.scss']
})
export class SingleCharacterComponent implements OnInit {
userData: object;
fname: string;
constructor(private randomUser: RandomUserService) {
this.randomUser.getNew().subscribe(data => {
this.userData = data[0];
})
}
ngOnInit(): void {
}
}
You are not parsing the returned data correctly in getNew().
The returned data looks like this:
So you need to access the user data like:
this.randomUser.getNew().subscribe(data => {
this.userData = data[0][0]; // note 2nd [0]
})
or for first name:
this.randomUser.getNew().subscribe(data => {
this.userData = data[0][0].name.first;
})
See stackblitz here: https://stackblitz.com/edit/so-http-parse?file=src/app/app.component.ts

Angular method returns undefined

As a beginner, I facing a problem with Angular and Observables. I have API for getting information about one specific restaurant in the database, but I have to get it with a POST request. I successfully get restaurantID from auth.service and another API when the restaurant is logged in, But when I tried to log restaurant in console, I get undefined. Uniformly I don't have permission to show API here. The code:
restaurant.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { Restaurant } from '../models/Restaurant';
import { LoggedRestaurant } from '../models/LoggedRestaurant';
#Injectable({
providedIn: 'root'
})
export class RestaurantService {
private restaurantUrl = 'xxxxxxxxxxxx';
public restaurant: Restaurant;
public loggedRestaurant: LoggedRestaurant
public restaurantID;
constructor(private http: HttpClient) { }
public getRestaurant(): Observable<LoggedRestaurant> {
return this.http.post<LoggedRestaurant>(this.restaurantUrl, this.restaurantID);
}
}
informacije.component.ts
import { Component, OnInit } from '#angular/core';
import { AuthService } from '../services/auth.service';
import { RestaurantService } from '../services/restaurant.service';
import { Restaurant } from '../models/Restaurant';
import { LoggedRestaurant } from '../models/LoggedRestaurant';
import { Observable } from 'rxjs';
#Component({
selector: 'app-informacije',
templateUrl: './informacije.component.html',
styleUrls: ['./informacije.component.scss']
})
export class InformacijeComponent implements OnInit {
restaurant: Restaurant;
loggedRestaurant: LoggedRestaurant;
restaurantID;
constructor(private restaurantService: RestaurantService, private authService: AuthService ) { }
getRestaurant() {
return this.restaurantService.getRestaurant()
}
ngOnInit() {
this.restaurant = this.authService.currRestaurant[0];
console.log(this.restaurant)
console.log(this.loggedRestaurant)
this.restaurantID = this.restaurant.id;
console.log(this.restaurantID)
this.restaurantService.restaurantID =this.restaurantID;
}
}
httpClient.post() returns an observable (RXJS). So you need to subscribe to that. Otherwise, you may use the async pipe.
in your html, you can try this,
<span>{{getRestaurant() | aync}}</span>
OR,
you can declare a variable in your ts like data, and,
this.restaurantService.getRestaurant().subscribe(payload => {
this.data = payload;
})
and in your html, you can add,
<span *ngIf="data">{{data}}</span>
You need to subscribe to your API call.
In informacije.component.ts
getRestaurant() {
return this.restaurantService.getRestaurant()
.subscribe(data => this.restaurant = data);
}
This will asign the value returned by your service to your restaurant field in an asynchronous fashion.
In ngOnInit() call getRestaurant as follows
async ngOnInit() {
let restaurant = await this.getRestaurant().toPromise();
...
}

Load json data in service and return it to component

I have a component with it's controller and template. I'm using a service to load data from a json file and return it to my component. The returned data should be looped within the template of the component. At the moment, the console.log() in my service shows me the correct data, so it loads everything from the json file. The console.log() in my component is wrong. I know, there are few questions with answers, but they didn't help me. Is there a problem with asynchron loading? Any ideas? I also attached the console errors on the bottom of the question.
test.json
[
{
"Id": 1,
"Name": "Item 1"
},
{
"Id": 2,
"Name": "Item 2"
},
{
"Id": 3,
"Name": "Item 3"
}
]
test.component.ts
import { Component, OnInit } from "#angular/core";
import { NavigationService } from "../../../services/navigation.service";
export type NavigationListModel = { Id: number; Name: string };
#Component({
selector: "test",
templateUrl: "./test.component.html",
styleUrls: ["./test.component.scss"]
})
export class TestComponent implements OnInit {
navigationList: Array<NavigationListModel>;
constructor(private _navService: NavigationService) {
this.navigationList = this._navService.loadNavigationList();
console.log(this.navigationList);
}
ngOnInit() {}
}
test.service.ts
import { Injectable } from "#angular/core";
import { Http, Response } from "#angular/http";
import 'rxjs/add/operator/map';
#Injectable()
export class NavigationService {
constructor(private http: Http) {
}
loadNavigationList() {
return this.http
.get("/assets/mock/test/test.json")
.map(data => data.json() as Array<NavigationService>)
.subscribe(data => {
console.log(data);
return data;
});
}
}
test.component.html
<div *ngFor="let item of navigationList">
{{item.Name}}
</div>
ERROR IN CONSOLE and CONSOLE.LOG() results:
When you create service that has return towards HTTP and it is async, you need to subscribe to that function where you call it. As you can see your console log tells you that it is subscription.
Try :
this._navService.loadNavigationList().subscribe((data: any) => {
console.log(data)
});
Or you can recreate your service that will return new Promise with resolve and error, I usually perfer to do it this way
loadNavigationList() {
return new Promise((error, resolve) => {
this.http
.get("/assets/mock/test/test.json")
.map(data => data.json() as Array<NavigationService>)
.subscribe((data) => {
// if api, check for errr
if(data.code == 500) {
error(something);
return
}
resolve(data)
});
});
}
Where you will call it
loadNavigationList().then((data) => {
}, (err) => {
})
You are looping through the navigationList field which is still undefined because of the asynchronous call (the response is not yet received).
To fix this problem you have to make your loadNavigationList() method return an observable of Array and use it in the test componennt with the async pipe inside the template:
test.service.ts:
import { Injectable } from "#angular/core";
import { Http, Response } from "#angular/http";
import 'rxjs/add/operator/map';
#Injectable()
export class NavigationService {
constructor(private http: Http) {
}
loadNavigationList() {
return this.http
.get("/assets/mock/test/test.json")
.map(data => data.json() as Array<NavigationService>);
}
}
test.component.ts:
import { Component, OnInit } from "#angular/core";
import { NavigationService } from "../../../services/navigation.service";
export type NavigationListModel = { Id: number; Name: string };
#Component({
selector: "test",
templateUrl: "./test.component.html",
styleUrls: ["./test.component.scss"]
})
export class TestComponent implements OnInit {
navigationListObservable$: Observable<Array<NavigationListModel>>;
constructor(private _navService: NavigationService) {
this.navigationList$ = this._navService.loadNavigationList();
}
ngOnInit() {}
}
test.component.html
<div *ngFor="let item of (navigationListObservable$ | async)">
{{item.Name}}
</div>

Cannot assign Object[] to Observable<Object[]>

I'm currently bumbling my way through an Angular 4 project. I've manageed to overcome most errors myself, so far, however I cannot figure out this one.
I am trying to use *ngFor (async) to display a list of Observable objects.
However, I get the error "Cannot assign Course[] to Observable< Course[] >", however I feel like my service is returning an Observable< Course[] >.
course-list.component.ts:
import { Component, OnInit } from '#angular/core';
import { Http } from '#angular/http';
import { CourseCardComponent } from '../course-card/course-card.component';
import { CourseCardService } from '../course-card/course-card.service';
import { CourseCard } from '../course-card/course-card.model';
import { Observable } from 'rxjs/Observable';
#Component({
selector: 'app-course-list',
templateUrl: './course-list.component.html',
styleUrls: ['./course-list.component.css']
})
export class CourseListComponent implements OnInit {
courseCards : Observable<CourseCard[]>;
loaded = false;
constructor(private http:Http, private coursecardService:CourseCardService) { }
ngOnInit() {
this.coursecardService.getCourses()
.subscribe(
courses => {
this.courseCards = courses;
console.log(this.courseCards);
this.loaded = true;
},
err => {
console.log("Error", err);
}
)
}
}
course-card.service.ts
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import { CourseCard } from './course-card.model';
#Injectable()
export class CourseCardService {
// Returns this JSON data:
// [{"firstName":"Jane"},{"firstName":"John"}]
private URL = '/api/getcourses';
constructor (private http: Http) {}
getCourses(): Observable<CourseCard[]> {
return this.http.get(this.URL)
.map((response) => {
let data = response.text() ? response.json():[{}];
if(data) {
return data;
}
}
)
.catch((error:any) => Observable.throw(error.json().error || 'Server error'));
}
}
And the HTML for the course-list component
Courses
<ul>
<li *ngFor="let course of courses|async">
<app-course-card [name]='course.name' [wordcount]=0></app-course-card>
</li>
</ul>
This part does return an Observable<CourseCard[]>:
this.coursecardService.getCourses()
But then you are manually subscribing to it, and inside of the subscribe, courses is of type CourseCard[]. So when you try to assign this.courseCards = courses;, that's when you're getting the type mismatch.
The async pipe will do the subscription for you, so you can change your code to:
ngOnInit() {
this.courseCards = this.coursecardService.getCourses();
}
Nevermind, I read more about the .subscribe method. It returns a subscription object, I just needed to change it to:
ngOnInit() {
this.courseCards = this.coursecardService.getCourses();
}
Is your list properties name is correct? let course of courses or supposed to be
let course of courseCards?
<ul>
<li *ngFor="let course of courseCards|async">
<app-course-card [name]='course.name' [wordcount]=0></app-course-card>
</li>
</ul>
Try like this :
getCourses(): Observable<CourseCard[]> {
return this.http.get(this.URL)
.map((response) => <CourseCard[]>response.json())
.catch((error: any) => Observable.throw(error.json().error || 'Server error'));
}

Categories

Resources