ANGULAR - Mapping nested JSON data from API - json

so I've been struggling for the past day or so with mapping the response from a mock API - I think I'm mapping it correctly but when I try to access the data it doesn't return anything in the HTML.
Please find my code below:
data.service.ts
import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { ConsentData, Prompt } from '#app/models/consent-data';
import { Observable } from 'rxjs';
#Injectable({
providedIn: 'root',
})
export class ConsentWebviewDataService {
constructor(private httpClient: HttpClient) {}
getConsentData(): Observable<ConsentData<Prompt>> {
return this.httpClient.get<ConsentData<Prompt>>(
'MY_API_URL',
);
}
}
data.ts (interface)
export interface ConsentData<Prompt> {
prompts: Prompt[];
}
export interface Prompt {
promptId: number;
headline: string;
body: string;
imageUrl: string;
consents: string[];
type: string;
}
app.component.ts
export class PromptComponent implements OnInit {
consentData: any;
constructor(private consentWebviewDataService: ConsentWebviewDataService) {}
ngOnInit(): void {
this.consentWebviewDataService.getConsentData().subscribe(data => {
this.consentData = data.prompts.map(consents => {
return {
promptId: consents.promptId,
headline: consents.headline,
body: consents.body,
imageUrl: consents.imageUrl,
consents: consents.consents,
type: consents.type,
};
});
});
}
}
Lastly here is the API JSON response:
{"prompts":[{"promptId":100,"headline":"Headline","body":"Body text.","imageUrl":"https://picsum.photos/200","consents":["Consent 1","Consent 2","Consent 3"],"type":"ConsentCampaign"}]}
From what I understand so far, after countless tutorials and resources, the getCosentData() function sends request to API, then in the component I subscribe to it, get the response, assign the response to the consentData variable, then map the contents of the response based on the interface / how the JSON response looks.
However, the problem is that I cannot access the mapped data in the HTML. I don't need it in a table, just need to get the mapped data.
I tried all variations such as {{ consentData.promptId }} which I mapped, and it returns ERROR TypeError: ctx.consentData is undefined. Tried {{ consents.promptId }} as well, etc. but nothing works.
What am I missing here? And apologies for the long question && thanks in advance for any help!

You mapped the response into a new array and trying to access it as an object
Try {{ consentData[0].promptId }} to get the id of first element

Related

How to extract data from SafeSubscriber?

I have to make an Angular application in which i get data from the back-end and display it on the front-end, but with some added hard-coded data.
My communication is between 2 files:
client.service.ts
import { Injectable } from '#angular/core';
import {HttpClient, HttpHeaders} from "#angular/common/http";
import {environment} from "../environments/environment";
import {catchError, map, Observable, of} from "rxjs";
const clientUrl = environment.apiUrl+'client';
#Injectable({providedIn: 'root'})
export class ClientService {
public optional: any;
constructor(private http: HttpClient) {}
getText(): Observable<any> {
console.log("it works!");
return this.http.get(clientUrl+"/getText").pipe(map(res => {
console.log(res);
this.optional = res.toString();
}));
}
}
and the second one:
client.component.ts
import { Component, OnInit } from '#angular/core';
import {ClientService} from "../client.service";
#Component({
selector: 'app-client',
templateUrl: './client.component.html',
styleUrls: ['./client.component.css']
})
export class ClientComponent implements OnInit {
public textResponse: any;
constructor(public service: ClientService) {}
ngOnInit(): void {}
getText() {
let text: any;
this.textResponse = this.service.getText().subscribe();
console.log(this.textResponse);
text = this.textResponse + "This text is added from code.";
console.log(text);
}
}
When i call "this.http.get(clientUrl+"/getText")" I get a SafeSubscriber object, from which i managed to get the data displayed in the console using the method ".subscribe(...)" with a "console.log()" inside of it. However, i did not find any method to extract the data out of this subscribe.
As the code above shows, i have tried to use pipe and map, but the local variable is returned as [Object object], and when i print it in the console i get either undefined, either nothing.
This is what my code currently displays:
it works! [client.service.ts:33]
SafeSubscriber {initialTeardown: undefined, closed: false, _parentage: null, _finalizers: Array(1), isStopped: false, …} [client.component.ts]
[object Object]This text is added from code. [client.component.ts]
{text: 'This text is read from a file.'} [client.service.ts]
I have also tried all the suggestions found in questions below:
angular 2 how to return data from subscribe
Angular observable retrieve data using subscribe
Does anyone know a method in which i could get the data out of the Subscribe?
You are missing the return keyword when mapping the response, looking at the console.log, you need the text property
getText(): Observable<any> {
console.log("it works!");
return this.http.get(clientUrl+"/getText").pipe(map(res => {
console.log(res);
this.optional = res.toString();
return res.text;
}));
}

Angular observable subscribe JSON parsing issue

I have a service performing http.get on a Drupal API and retrieving JSON data.
The component utilising that JSON data keeps generating the following error:
ERROR in src/app/form-test/form-test.component.ts(18,28): error TS2551: Property 'included' does not exist on type 'Question[]'. Did you mean 'includes'?
From the following code:
constructor(private dataService: QuizService) { }
ngOnInit() {
this.dataService.fetch().subscribe(data => {
this.jsondata = data.included[0].attributes.field_json;
console.log(data, ': DATA');
});
}
I don't understand why there is a problem with the JSON and why it's trying to find includes instead of included in the JSON structure. Below is a screenshot of a sample of the JSON:
I have confirmed the structure of the JSON data (as confirmed from the image above), also from console logging the JSON data and that the API URL is live at the time Ay angular app is attempting to call it.
Can anyone advice what is the cause of this error and how can I resolve it?
UPDATE:
quiz.service.ts:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Observable } from 'rxjs';
export interface Question {
// title: string;
question: string;
included: any[];
}
#Injectable({
providedIn: 'root'
})
export class QuizService {
// tslint:disable-next-line: max-line-length
private quizURL: string = 'http://drupal-8-composer-drupal-test.com/jsonapi/node/quiz/31f020f7-34d9-4b9a-bd2b-0d567eb285dc/?include=field_questions&fields%5Bnode--quiz%5D=title,drupal_internal__nid,body&fields%5Bnode--question%5D=title,field_processed,body,field_options,field_json';
constructor(private httpClient: HttpClient) { }
fetch(): Observable<Question[]> {
return this.httpClient.get<Question[]>( this.quizURL );
}
}
The error states that data has type Question[]. It is an array, not an object. Typescript compiler tries to find an included variable in array and there's none. So it gives you an error.
Your JSON structure contains an array of questions in the included field. So the type which the fetch returns should be like { included: Question[] }:
fetch(): Observable<{ included: Question[] }> {
return this.httpClient.get<{ included: Question[] }>( this.quizURL );
}
Or you can process the response in service and return questions only:
fetch(): Observable<Question[]> {
return this.httpClient.get(this.quizURL)
.pipe(map((data: { included: Question[] }) => data.included));
}
.map operator gets the whole response object, extracts only questions and returns them as array.

How to get data of YAML file in the Angular 7 array of objects

I know how to get a JSON file and to show it in the array in Angular 7 but I am having a problem with the YAML file I have searched and I really do not know how to get in the array the YAML
For the moment I have tried this but it is coming with errors.
Service
#Injectable({
providedIn: 'root'
})
export class getYamlDataService {
constructor(private http: HttpClient) {
this.getJson().subscribe(data => {
console.log(data);
})
}
public getJson(): Observable<any> {
return this.http.get("./assets/swagger/swagger.yaml");
}
}
Component.ts
constructor(private getYamlData: getYamlDataService) {}
ngOnInit() {
this.getYamlData.getJson().subscribe(data => {
console.log(data);
});
Error
Http failure during parsing for http://localhost:4200/assets/swagger/swagger.yaml
But when I open this localhost then it is showing in browser yaml file.
This is due to your response being parsed as a JSON Object instead of returning a plain string. If you look at the API of HttpClient.get(string) without an options parameter, you will see that the response is observed as a json (found here, 15th overload)
get<T>(url: string, options?: { headers?: HttpHeaders | { [header: string]: string | string[]; }; observe?: "body"; params?: HttpParams | { [param: string]: string | string[]; }; reportProgress?: boolean; responseType?: "json"; withCredentials?: boolean; }): Observable<T>
You have to specify the return type you want (in this case most likely "text")
#Injectable({
providedIn: 'root'
})
export class getYamlDataService {
constructor(private http: HttpClient) {
this.getJson().subscribe(data => {
console.log(data);
})
}
public getJson(): Observable<any> {
return this.http.get("./assets/swagger/swagger.yaml", {
observe: 'body',
responseType: "text"; // This one here tells HttpClient to parse it as text, not as JSON
});
}
}
If you want to use that yaml as a JavaScript Object, you will have to parse it yourself. Luckily, there are already libararies like yaml.js that you can leverage for this. First, install the libarary npm i --save yamljs and then use it like this:
import {parse} from 'yamljs';
import {map} from 'rxjs/operators';
#Injectable({
providedIn: 'root'
})
export class getYamlDataService {
constructor(private http: HttpClient) {
this.getJson().subscribe(data => {
console.log(data);
})
}
public getJson(): Observable<any> {
return this.http.get("./assets/swagger/swagger.yaml", {
observe: 'body',
responseType: "text" // This one here tells HttpClient to parse it as text, not as JSON
}).pipe(
// Map Yaml to JavaScript Object
map(yamlString => parse(yamlString))
);
}
}
Here is a working StackBlitz showcasing this.
Edit: Added example for parsing the returned string to a JavaScript Object
Edit2: Added StackBlitz example

Angular http request with interface: statusText: "Unknown Error"

I am trying to call an example API (https://jsonplaceholder.typicode.com/posts) in Angular via the use of an interface.
However I am getting the following error. ERROR HttpErrorResponse {headers: HttpHeaders, status: 0, statusText: "Unknown Error", url: null, ok: false, …}
My code for my TS file is below
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import { HttpClient } from '#angular/common/http';
interface Post {
title: string;
body: string;
};
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
constructor(public navCtrl: NavController, private http: HttpClient) {
}
ionViewDidLoad() {
this.getData();
}
getData() {
this.http.get<Post>('https://jsonplaceholder.typicode.com/posts').subscribe(res => {
let postTitle = res.title;
console.log(postTitle);
});
}
}
well, your code has a few problems for one res is of Array type and if you want to access your objects property you'll have to loop through it (perhaps something like this:)
let postTitle = [];
this.http.get<Post>('https://jsonplaceholder.typicode.com/posts').subscribe(res => {
res.forEach(element => {
postTitle.push(element);
});
console.log(postTitle);
});
and I strongly recommend to call an API via a service don't do it in a component.
So I tried to replicate this with
https://stackblitz.com/edit/angular-njzmwr
I found an issue that, your current api is returning data as an array so either selects the data by the filter from array or something else.
pls check the above-mentioned URL
The API returns and Array of PostS. Try:
getData() {
this.http.get<Post[]>('https://jsonplaceholder.typicode.com/posts').subscribe(res => {
let postTitle = res[0].title;
console.log(postTitle);
});
}
HTH

Loading data from a json file with an Interface

I'm writing a little ionic app for learning purposes and I would like to load data from a json file and assign it to an Interface that describes the data. But I'm struggling with getting it the right way:
import { Component } from "#angular/core";
import { HttpClient} from "#angular/common/http";
export interface PhonebookEntry {
name: string,
telephone: string,
description: string
}
#Component({
selector: 'page-phonebook',
templateUrl: 'phonebook.html'
})
export class PhonebookPage {
entries: Array<PhonebookEntry>;
constructor(public http: HttpClient) {
this.load_entries('assets/json/phonebook.json');
};
load_entries(filePath: string) {
return this.http.get(filePath)
.subscribe(
data => this.entries = data
);
};
}
I think only the line data => this.entries = data is wrong (also the IDE is telling me that), but I don't know to do this right and can't find documentation describing the correct way. If there actually is some I would be glad to know where I can find ressources about this.
subscribe return the response as an object, not as an array. So entries type should be changed.
entries: PhonebookEntry;
In the subscribe, need to assign a type for response data.
load_entries(filePath: string) {
return this.http.get(filePath)
.subscribe(
(data: PhonebookEntry) => this.entries = data // or use any type
);
};
Demo