Display Subscribe Data in Angular 4 - json

I need help in displaying the output of subscribe from an api in Angular 4. How can i do this since I wrote data.data.data but it says property data doesnt exist on type object. How would i output it in the browser? Here's my code below and the api picture below
import { Component, OnInit } from '#angular/core';
import { NewsService } from '../news.service';
#Component({
selector: 'app-news-list',
templateUrl: './news-list.component.html',
styleUrls: ['./news-list.component.css']
})
export class NewsListComponent implements OnInit {
constructor(private newsService: NewsService) { }
ngOnInit() {
this.newsService.getNews()
.subscribe(
data => {
alert("News Success");
console.log(data);
},
error => {
alert("ERROR");
});
}
}

create a property in component
myData: any[] = [];
and in your subscriber function
import { Component, OnInit } from '#angular/core';
import { NewsService } from '../news.service';
#Component({
selector: 'app-news-list',
templateUrl: './news-list.component.html',
styleUrls: ['./news-list.component.css']
})
export class NewsListComponent implements OnInit {
constructor(private newsService: NewsService) { }
ngOnInit() {
this.newsService.getNews()
.subscribe(
(res: any) => {
alert("News Success");
this.myData = res.data;
// Where you find the array res.data or res.data.data
console.log('res is ', res.data);
},
error => {
alert("ERROR");
});
}
}
and in your template
1) option to view JSON
<pre>{{myData | json}}</pre>
2) option to loop if you got the array
<div *ngFor="let d of myData">
{{d}}
</div>

Your data is type of array,
Create a variable of type any
myData : any;
and assign the data to myData,
this.newsService
.getNews()
.subscribe(
data => {
this.myData = data.data;
},
error => {
alert("ERROR");
}
);
you can use ngFor to iterate over array and display in the HTML
<li *ngFor="let item of myData">
{{item}}
</li>

You need to do it like this
ngOnInit() {
this.newsService.getNews()
.subscribe(
data => {
data = data.json();
console.log(data.data);
},
error => {
alert("ERROR");
});
}
the data.json() part is important, it converts the response into proper json so can access its data.
Now you can assign it to instance variable like this
this.myArrayData = data.data
inside your subscribe() method
Then in your template
<div *ngFor="let data of myArrayData">
<!-- do whatever with the data properties -->
</div>

Related

Ionic5/Angular - Error trying to diff '[object Object]'. Only arrays and iterables are allowed

I'm getting this error when I want to display firebase subcollections in HTML. It does display in console but not in UI.
I'm getting this error. Console image
TS file
import { Component, OnInit } from '#angular/core';
import { AuthService } from '../services/auth.service';
import { Router } from '#angular/router';
import { AngularFirestore } from '#angular/fire/compat/firestore';
#Component({
selector: 'app-my-reservations',
templateUrl: './my-reservations.page.html',
styleUrls: ['./my-reservations.page.scss'],
})
export class MyReservationsPage implements OnInit {
user: any;
userId: string;
storedData: any = [];
constructor(
private auth: AuthService,
private router: Router,
private afs: AngularFirestore
) {}
ngOnInit() {
this.auth.user$.subscribe((user) => {
this.userId = user.userId;
});
}
fetchBookings() {
this.afs
.collection('user')
.doc(this.userId)
.collection('BookingHistory')
.get()
.subscribe((querySnapshot) => {
querySnapshot.forEach((doc) => {
console.log(doc.id, ' => ', doc.data());
this.storedData = querySnapshot;
});
});
}
}
HTML file
<ion-item *ngFor="let data of storedData">
<ion-label>{{data.TimeSlot}}</ion-label>
<ion-label>{{data.BookedAt}}</ion-label>
<ion-label>{{data.BookingDate}}</ion-label>
</ion-item>
I'd try something like this,
As long as the querySnapshot returns array of storedData without any nested objects, you can set it directly if not you have to read it through the correct entry from the response structure and read the values from the list accordingly in your HTML template
fetchBookings() {
this.afs
.collection('user')
.doc(this.userId)
.collection('BookingHistory')
.get()
.subscribe((querySnapshot) => {
querySnapshot.forEach((doc) => { // or you can remove this forEach() and set the querySnapshot (if it returns an array of storedData) directly
console.log(doc.id, ' => ', doc.data());
this.storedData.push(doc);
});
});
}
}

Angular 9 data from api call isn't populating dropdown

Having some problems with filling my drop down menu with data from my API. I need help figuring out what I am doing wrong because I am getting no errors in the console.
Service.ts method call:
#Injectable({
providedIn: 'root'
})
export class OrderExceptionReportService extends ApiServiceBase {
private apiRoute: string = 'exception-report';
public sessionData: ExceptionReportSessionData[];
constructor(http: HttpClient, configService: ConfigService) {
super(http, configService);
}
public async GetExceptionReportSessionData(): Promise<ExceptionReportSessionData[]> {
if (!this.appSettings) {
this.appSettings = await this.configService.loadConfig().toPromise();
}
return await this.http.get<ExceptionReportSessionData[]>(this.appSettings.apiSetting.apiAddress + this.apiRoute + '/session-data')
.pipe(
retry(1),
catchError(this.handleError)
)
.toPromise()
}
}
component.ts file:
#Component({
selector: 'app-order-exception-report',
templateUrl: './order-exception-report.component.html',
styleUrls: ['./order-exception-report.component.scss']
})
export class OrderExceptionReportComponent implements OnInit {
public sessionData: ExceptionReportSessionData[];
constructor(private orderExceptionReportService: OrderExceptionReportService) {
}
getExceptionReportSessionData() {
this.orderExceptionReportService.GetExceptionReportSessionData()
.then(
data => {
this.sessionData = data;
});
}
ngOnInit() {
}
}
html where I need the data:
<div class="input-group">
<select class="custom-select" id="inputGroupSelect01">
<option value="" *ngFor="let session of sessionData">{{session.SessionName}}</option>
</select>
</div>
Interface.ts file:
export interface ExceptionReportSessionData {
SessionName: string,
ReportFiles: Array<string>
}
From your comments, it appears the this.configService.loadConfig() returns an HTTP observable as well.
In that case, you could avoid using promises and streamline the process using RxJS method iff to check if the this.appSettings variable is defined and switchMap operator to switch to the target request once appSettings is retrieved.
Try the following
public GetExceptionReportSessionData(): Observable<ExceptionReportSessionData[]> {
return iff(() =>
this.appSettings,
of(this.appSettings),
this.configService.loadConfig().pipe(tap(appSettings => this.appSettings = appSettings))).pipe(
switchMap(appSettings => this.http.get<ExceptionReportSessionData[]>(appSettings.apiSetting.apiAddress + this.apiRoute + '/session-data')),
retry(1),
catchError(this.handleError)
);
}
tap operator is used to tap into the result and store it into this.appSettings variable. of method is used to send an observable of this.appSettings since switchMap operator expects an observable.
You could then subscribe to the function in the component
export class OrderExceptionReportComponent implements OnInit {
public sessionData: ExceptionReportSessionData[];
constructor(private orderExceptionReportService: OrderExceptionReportService) {}
getExceptionReportSessionData() {
this.orderExceptionReportService.GetExceptionReportSessionData().subscribe(
data => { this.sessionData = data; },
error => { }
);
}
ngOnInit() {
this.getExceptionReportSessionData();
}
}

ERROR Error: InvalidPipeArgument: '[object Object]' for pipe 'AsyncPipe' even when returning an observable

I found few questions with same title and, as far as I could see, some of them are suggesting that the solution is basically return an Observable instead of an array (others are about FireBase which isn't my case). Well, as far I am concern, the code below does return an Observable (look at "getServerSentEvent(): Observable {return Observable.create ...")
My final goal is get all events from a stream returned from a Rest WebFlux. I didn't past bellow the backend because I am pretty sure the issue is related to some mistake in Angular.
On top of that, I can debug and see the events properlly comming to extratos$ from app.component.ts(see image bellow).
Whole logs
core.js:6185 ERROR Error: InvalidPipeArgument: '[object Object]' for pipe 'AsyncPipe'
at invalidPipeArgumentError (common.js:5743)
at AsyncPipe._selectStrategy (common.js:5920)
at AsyncPipe._subscribe (common.js:5901)
at AsyncPipe.transform (common.js:5879)
at Module.ɵɵpipeBind1 (core.js:36653)
at AppComponent_Template (app.component.html:8)
at executeTemplate (core.js:11949)
at refreshView (core.js:11796)
at refreshComponent (core.js:13229)
at refreshChildComponents (core.js:11527)
app.component.ts
import { Component, OnInit } from '#angular/core';
import { AppService } from './app.service';
import { SseService } from './sse.service';
import { Extrato } from './extrato';
import { Observable } from 'rxjs';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
providers: [SseService],
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
//extratos: any;
extratos$ : Observable<any>;
constructor(private appService: AppService, private sseService: SseService) { }
ngOnInit() {
this.getExtratoStream();
}
getExtratoStream(): void {
this.sseService
.getServerSentEvent("http://localhost:8080/extrato")
.subscribe(
data => {
this.extratos$ = data;
}
);
}
}
sse.service.ts
import { Injectable, NgZone } from '#angular/core';
import { Observable } from 'rxjs';
import { Extrato } from './extrato';
#Injectable({
providedIn: "root"
})
export class SseService {
extratos: Extrato[] = [];
constructor(private _zone: NgZone) { }
//getServerSentEvent(url: string): Observable<Array<Extrato>> {
getServerSentEvent(url: string): Observable<any> {
return Observable.create(observer => {
const eventSource = this.getEventSource(url);
eventSource.onmessage = event => {
this._zone.run(() => {
let json = JSON.parse(event.data);
this.extratos.push(new Extrato(json['id'], json['descricao'], json['valor']));
observer.next(this.extratos);
});
};
eventSource.onerror = (error) => {
if (eventSource.readyState === 0) {
console.log('The stream has been closed by the server.');
eventSource.close();
observer.complete();
} else {
observer.error('EventSource error: ' + error);
}
}
});
}
private getEventSource(url: string): EventSource {
return new EventSource(url);
}
}
app.component.html
<h1>Extrato Stream</h1>
<div *ngFor="let ext of extratos$ | async">
<div>{{ext.descricao}}</div>
</div>
Evidence that the observable extratos$ is filled in
When you write this observer.next(this.extratos);, that means this.extratos is what you get on component side in the data argument of the callback, so when you do this this.extratos$ = data; you are actually storing the extratos Array. TypeScript doesn't complain about it, probably because it's not smart enough to infer the types when you build an Observable from scratch like you did.
Try this:
this.extratos$ = this.sseService
.getServerSentEvent("http://localhost:8080/extrato");
and in the template: <div *ngFor="let ext of extratos$ | async">

How to use one component data in another component in angular 6?

I have a component.ts file which is making a http call & retrieving json data as response. I need to use this response in another component.ts file. Can anyone tell me how to process this?
first component.ts:
#Component({
selector: 'app-cat',
templateUrl: './first.component.html',
styleUrls: ['./first.component.css']
})
export class firstComponent extends Lifecycle {
this.http.get('/name',{responseType:"json"}).subscribe(
response => {
console.log("data :"+response);
console.log("data stringify:"+JSON.stringify(response));
});
}
I need to use the json content which is in the response in my second component file. Can anybody tell me how to proceed this in angular6?
****Create separate service for making calls and in that service create a method as such
public getData(): Observable<> {
return this.http.get('/name',{responseType:"json"}).subscribe(
response => {
console.log("data :"+response);
console.log("data stringify:"+JSON.stringify(response));
});
}
****And in your component declare service in constructor //don't forget to import it
public jsonData:any;
constructor(private Service: Service ) {
}
getData() {
this.Service.getData().subscribe(data => {
console.log("Data is ",data);
this.jsonData = data;
},
error => console.log(error)
);
}
Finally,you can use jsonData to work with your data.
Parent to Child: Sharing Data via Input
parent.component.ts
import { Component } from '#angular/core';
#Component({
selector: 'app-parent',
template: `
<app-child [childMessage]="parentMessage"></app-child>
`,
styleUrls: ['./parent.component.css']
})
export class ParentComponent{
parentMessage = "message from parent"
constructor() { }
}
child.component.ts
import { Component, Input } from '#angular/core';
#Component({
selector: 'app-child',
template: `
Say {{ message }}
`,
styleUrls: ['./child.component.css']
})
export class ChildComponent {
#Input() childMessage: string;
constructor() { }
}
Sharing Data via Output() and EventEmitter
parent.component.ts
import { Component } from '#angular/core';
#Component({
selector: 'app-parent',
template: `
Message: {{message}}
<app-child (messageEvent)="receiveMessage($event)"></app-child>
`,
styleUrls: ['./parent.component.css']
})
export class ParentComponent {
constructor() { }
message:string;
receiveMessage($event) {
this.message = $event
}
}
child.component.ts
import { Component, Output, EventEmitter } from '#angular/core';
#Component({
selector: 'app-child',
template: `
<button (click)="sendMessage()">Send Message</button>
`,
styleUrls: ['./child.component.css']
})
export class ChildComponent {
message: string = "Hola Mundo!"
#Output() messageEvent = new EventEmitter<string>();
constructor() { }
sendMessage() {
this.messageEvent.emit(this.message)
}
}
please visit https://angularfirebase.com/lessons/sharing-data-between-angular-components-four-methods/ for other methods.
Solution 1 using a common injectible service
Shared.service.ts
#Injectible()
class SharedService {
function getData():any{
return this.http.get('/name',{responseType:"json"}).subscribe(
response => {
console.log("data :"+response);
console.log("data stringify:"+JSON.stringify(response));
});
}
}
Solution 2 using a parent child component
Second.component.ts
import { Component } from '#angular/core';
#Component({
selector: 'app-first-component',
template: `<p>{{data}}</p>`
})
export class SecondComponent{
data:any={};
ngOnInit(){this.getData();}
function getData():any{
this.http.get('/name',{responseType:"json"}).subscribe(
response => {
console.log("data :"+response);
console.log("data stringify:"+JSON.stringify(response));
this.data=data
});
}
}
parent.component.ts
import { Component } from '#angular/core';
import { SecondComponent } from './second.component';
#Component({
selector: 'app-first-component',
template: `
<h3>Get data (via local variable)</h3>
<button (click)="second.getData()">GetData</button>
<app-first-component #second></app-first-component>
`
})
export class FirstComponent{ }
Use Input & Output Decorators
Basic concept ---> DEMO
app.component.html:
<app-component1 (elm)="catch1Data($event)">
</app-component1>
<app-component2 [elm]="datatocomp2" *ngIf="datatocomp2"></app-component2>
parent component : {{datatocomp2 | json}}
app.component.ts:
datatocomp2: any;
catch1Data(data) {
console.log(data)
this.datatocomp2 = data;
}
component1.ts:
#Output () elm : EventEmitter<any> = new EventEmitter<any>();
objectData: any;
constructor() { }
ngOnInit() {
let objectData = {
comp: 'component 1',
data: 'anything'
}
this.objectData = objectData;
this.elm.emit(objectData)
}
component2.ts:
#Input() elm: any;
constructor() { }
ngOnInit() {
console.log(this.elm);
}
You can create store service for your 'global' data:
store.service.ts
import { Injectable } from '#angular/core';
#Injectable()
export class StoreService {
protected store: Map<string, any> = new Map();
constructor() { }
public get(key: string): any {
return this.store.get(key);
}
public set(key: string, value: any) {
this.store.set(key, value);
}
}
And then in yours component (lets call it X) you save data to store:
x.component.ts
import { Component, OnInit } from '#angular/core';
import { HttpClinet } from '#angular/common/http';
import { StoreService } from './store-service.service.ts';
#Component({
selector: 'app-x',
templateUrl: './x.component.html',
styleUrls: ['./x.component.css']
})
export class XComponent implements OnInit {
constructor(
private store: StoreService,
private http: HttpClient,
) { }
ngOnInit() {
}
getResource() {
this.http.get('/name',{responseType:"json"}).subscribe(
response => {
this.store.set('response', response);
console.log("data :"+response);
console.log("data stringify:"+JSON.stringify(response));
});
}
And then in yours other component (lets call it Y) you get your data:
y.component.ts
import { Component, OnInit } from '#angular/core';
import { StoreService } from './store-service.service.ts';
#Component({
selector: 'app-y',
templateUrl: './y.component.html',
styleUrls: ['./y.component.css']
})
export class YComponent implements OnInit {
constructor(
private store: StoreService
) { }
ngOnInit() {
}
getSavedResponse() {
// ask store for the resource
return this.store.get('response');
});
}
This is just simple example, if you know the structure of your response got by http call it would be good idea to make model of it.
Using the store any component can get or set store data.
If you need something more complex look for: #ngrx/store
Cases when you would not need store service:
If you do that http call in parent component then you can use child inputs to pass the data.
If you make that call in child component then use #Output and EventEmitter, to pass up the data (just one level, you can not do this to pass to grandparent)
Regards.

HTTP Request Angular 2

I'm trying to make a request from an external API in angular 2.
I want to manage the data request in 2 files and display the result as json.
My data-output component looks like this:
import {Component} from '#angular/core'
import {DataService} from './datavisualisation.service'
#Component({
selector: 'app-datavisualisation-output',
template: `
`
})
export class DatavisualisationOutput {
constructor(dataservice: DataService) {
dataservice.data
.subscribe(
data => this.data = data,
console.error,
() => console.log('Look at this:' + data)
);
}
}
My second file for the service looks like this:
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import 'rxjs/add/operator/map';
#Injectable()
export class DataService {
constructor(http:Http) {
this.data = http.get('http;//...API')
.map(response => response.json());
}
}
...but the console shows the following error:
components/datavisualisation/dataservices/datavisualisation.output.service.ts:12:26
Property 'data' does not exist on type 'DataService'.
components/datavisualisation/dataservices/datavisualisation.output.service.ts:14:29
Property 'data' does not exist on type 'DatavisualisationOutput'.
components/datavisualisation/dataservices/datavisualisation.output.service.ts:16:43 Cannot find name 'data'.
components/datavisualisation/dataservices/datavisualisation.service.ts:8:13
Property 'data' does not exist on type 'DataService'.
What am i doing wrong here?
You should define the dataproperty on your DatavisualisationOutput component:
export class DatavisualisationOutput {
public data: any; //this one
constructor(dataservice: DataService) {
dataservice.data
.subscribe(
data => this.data = data,
console.error,
() => console.log('Look at this:' + data)
);
}
}
and on your DataService:
#Injectable()
export class DataService {
public data: any;
constructor(http:Http) {
this.data = http.get('http;//...API')
.map(response => response.json());
}
}
and on DatavisualisationOutput... Just.. always define any property you access with this.
As #PierreDuc already said you should define that variable inside component class to make it available inside Class context.
Also you should create a method inside a service which would be responsible for data. Just make an call same method from another component and it will return the data which has retrieved last time.
Code
#Injectable()
export class DataService {
data: any;
constructor(http:Http) {
}
getData(){
if(this.data) return Observable.of(this.data)
else
return http.get('http;//...API')
.flatMap(response => {
this.data = response.json();
return Observable.of(this.data)
);
}
}
Component
export class DatavisualisationOutput {
myData: any;
constructor(private dataservice: DataService) {
dataservice.data
.subscribe(
data => this.data = data,
console.error,
() => console.log('Look at this:' + data)
);
}
ngOnInit(){
dataservice.getData().subscribe(
data => myData = data
)
}
}