Angular 4 httpclient mapping observable to nested json - json

With the help of the forum I was able to get my httpclient observable mapping issue sorted with this syntax;
this._http.get<DomainMetaData>(serviceURL);
which works great! However, I have a json response coming back from the server which is nested and wonder if I can use the same syntax as I'm currently using or if I need to now manually .map the response into my classes?
Based on posts I've seen here on SO I've created two classes to represent the nested structure of the response JSON (see below).
The function call...
getDomainMetaData(domain): Observable<DomainMetaData> {
let serviceURL = "http://localhost:3000/selectdomains?domain=" + domain;
return this._http.get<DomainMetaData>(serviceURL);
}
The classes...
export class DomainMetaDataAttr {
constructor(public name: string,
public value: string) {
}
}
export class DomainMetaData {
constructor(public name: string,
public attributes: DomainMetaDataAttr[]) {
}
}
An example of the json...
//DomainMetaData
// {
// "ResponseMetadata": {
// "RequestId": "11f000bf-0dff-8a2a-31ff-8631a9f25b5b",
// "BoxUsage": "0.0008183545"
// },
// "Items": [
// {
// "Name": "2",
// "Attributes": [
// {
// "Name": "Document Date",
// "Value": "22/03/13"
// },
// {
// "Name": "Document Ref",
// "Value": "Doc test"
// }
// ]
// },
I love the neatness and simplicity of my current solution but I appreciate I may now have to change my code!
Many Thanks.

If I understand correctly you want to know how to use the JSON response from an HttpClient call.
I currently approach it like this:
// x.service.ts
getData() {
return this.http.get(URL);
}
// x.component.ts
this.service.getData().subscribe(res => {
if (res['data']) {
const data = res['data'];
// do whatever with the data
}
});
With the above approach you can run whatever methods / filters you want on the JSON e.g. map over the array and pull data out / mutate it, etc. Not sure if it's necessary to create additional classes to deal with the nested JSON data.

Oops! The code I posted actually works, I just wasn't referencing the results in the attributes array correctly.
Thanks for taking the time to look at this.

Related

Change JSON structure to send in API post request

I am making an API POST call in Angular 8. I have to send a JSON object in the call which should be of structure:
-{}JSON
-{}data
-[]exp
+{} 0
+{} 1
but I am sending data in this format:
-[]JSON
+{} 0
+{} 1
in my typescript I am getting two objects {}0, {}1 in array called: receivedData then I am storing the data like:
this.changedData = this.receivedData;
this.postService.postMethod(this.headers, this.changedData)
in my postService:
postMethod(header, changedData): Observable<any[]> {
return this.http.post<any>(`the url here`, changedData, {headers: header, responseType: 'text' as 'json'})
.pipe(map(response => {
return response;
}))
}
how to send data in the mentioned format? I want the json structure of changedDetails to be as mentioned on the top with the same naming convention like: {}data and []exp How can I push receivedData objects into exp[] which I can then send into data{} which will then be entirely pushed into changedDetails {}
Just so we're on the same page, I'm imagining you're receiving data with the following shape:
[ { ... }, { ... } ]
And you want to transform it to this shape:
{
data: {
exp: [ { ... }, { ... } ]
}
}
(Let me know if this is not the case.)
If this is correct, than the transformation is quite straightfoward: simply create a new object literal like so:
this.changedData = {
data: {
exp: this.receivedData,
},
};

Get json objects from server

I am trying to get a JSON object API from server and my json start with objects and l am not familiar with Json response start with objects immediately .
Json url
{
"ABQ": {
"airport": {
"name": "Albuquerque International Airport",
"code": {
"iata": "ABQ",
"icao": "KABQ"
}
}
},
"ACE": {
"airport": {
"name": "Lanzarote Airport",
"code": {
"iata": "ACE",
"icao": "GCRR"
}
}
},
"ADB": {
"airport": {
"name": "Izmir Adnan Menderes International Airport",
"code": {
"iata": "ADB",
"icao": "LTBJ"
}
}
}
}
My code :
Data :any
getData(){
this.http.get("xxxxxxxx/", {}, {}).then(data =>{
this.Data = JSON.parse(data.data)
console.log(this.Data)
})
}
HTML
<div class="ion-padding">
{{Data.airport.name}}
</div>
l got error
ERROR TypeError: Cannot read property 'airport' of undefined at object.eval
how can l get the data json response ?
You have two problems in your example:
You're trying to render the data before it was loaded. And as long as you're trying to get user attributes you get exceptions like Cannot read property XXX of undefined
You are ignoring intermediate properties of your data object.
There is more than one way to skin a cat and there are several possible solutions. One of them I've implemented in this example https://stackblitz.com/edit/angular-zuqbry.
I'm using async filter to wait for the server response to be resolved
<p *ngFor="let item of data | async">
{{item.airport.name}}
</p>
I'm iterating over Object keys in order to get all there 'dynamic keys' and fetch data from the data object
this.data = http.get()
.pipe(
map(response => {
const keys = Object.keys(response);
return keys.map(key => response[key]);
})
);
Please, note that http is an object of CustomHttpService that I've created to provide data from hardcoded file, however it works very similar to original HttpClient.

TypeScript fetch data from API

I want the code below to be able to fetch JSON data from a simple API I have set up.
export class FaqFakeDb
{
public static data = fetch('https://#######.co.uk/api/faq/');
}
This is for a FAQ page and its meant to be pulling data from the API. I am new to typescript and Angular so forgive me if I've made a simple mistake.
I want it to function like this:
export class FaqFakeDb
{
public static data = [
{
"id":"1",
"question":"test1",
"answer":"test1"
},
{
"id":"2",
"question":"test1",
"answer":"test1"
},
{
"id":"3",
"question":"test1",
"answer":"test1"
}
];
}
Any help will me much appretiated :)
fetch returns a promise, so data will be a promise. Any time you want to access data you will have to use async/await or .then or something else to handle promises. You also don't have any error handling in case your API call fails.
One example of how you could handle this would be:
async ngOnInit() {
try {
const response = await FaqFakeDb.data;
this.quesitons = await response.json();
} catch {
// fetch encountered an error; you can handle it here.
}
}
However, I would recommend that you use Angular's built in HttpClientModule instead and make the API call as it's needed (in the component that needs it) rather than when an arbitrary class is declared.

Angular 2: Access object data

In my Angular RC2 app I make an observable HTTP call that returns the following JSON to me from the API:
{
"success":true,
"data":
{
"Type": 1
"Details":{
"Id":"123",
"Name":"test",
"Description":"test"
}
}
}
I map the data like this:
this._myService.get(id)
.subscribe(
(data) => {
this.details = data.Details;
this.type = data.Type;
},
(error) => {
this.setError(error);
}
);
How do I access the values inside the "Details" object from here?
I tried:
{{details.Name}}
But that won't work and I can't use ngFor to loop it either.
You could use the Elvis operator for this:
{{details?.Name}}
As a matter of fact, you load your data asynchronously so details is undefined at the beginning.

Reformat API data to Ember friendly array of objects | Ember data with unconventional endpoint

This is a question about molding some API data to fit some needs. I've heard it called "munging." I guess the heart of if is really re-formatting some JSON, but It would be ideal to do it the Ember data way...
I'm getting this data in an Emberjs setting - but it shouldn't really matter - ajax, ic-ajax, fetch, etc... I'm getting some data:
...
model: function() {
var libraryData = ajax({
url: endPoint,
type: 'GET',
dataType: 'jsonp'
});
// or most likely the ember-data way
// this.store.findAll(...
console.log(libraryData);
return libraryData;
}
...
The URL is getting me something like this:
var widgetResults = {
"settings": {
"amazonchoice":null,
"show":{
"showCovers":null,
"showAuthors":null
},
"style":null,
"domain":"www.librarything.com",
"textsnippets":{
"by":"by",
"Tagged":"Tagged","readreview":"read review","stars":"stars"
}
},
"books":{
"116429012":{
"book_id":"116429012",
"title":"The Book of Three (The Chronicles of Prydain Book 1)",
"author_lf":"Alexander, Lloyd",
"author_fl":"Lloyd Alexander",
// ...
The promise that is actually returned is slightly different.
My goal is to get to those books and iterate over them - but in my case it wants an array. that #each loops over must be an Array. You passed {settings: [object Object], books: [object Object]} - which makes sense.
In and ideal API the endpoint would be / http:/site.com/api/v2/books
and retrieve the data in this format:
{
"book_id":"116428944",
"title":"The Phantom Tollbooth",
"author_lf":"Juster, Norton",
"author_fl":"Norton Juster",
...
},
{
"book_id":"116428944",
"title":"The Phantom Tollbooth",
"author_lf":"Juster, Norton",
"author_fl":"Norton Juster",
...
},
{
... etc.
I would expect to just drill down with dot notation, or to use some findAll() but I'm just shooting in the dark. Librarything in specific is almost done with their new API - but suggest that I should be able to loop through this data and reformat it in an ember friendly way. I have just looped through and returned an array in this codepen - but haven't had luck porting it... something about the returned promise is mysterious to me.
How should I go about this? am I pointed in the wrong direction?
I've tried using the RESTAdapter - but didn't have much luck dealing with more unconventional endpoints.
Custom Adapters / Serializers ?
this article just appeared: "Fit any backend into ember with custom adapters and serializers
Full url with endpoint in question
model (just title to test)
import DS from 'ember-data';
export default DS.Model.extend({
title: DS.attr('string')
});
route ( per #Artych )
export default Ember.Route.extend({
model() {
$.ajax({
url: endPoint,
type: 'GET',
dataType: 'jsonp'
}).then((widgetResults) => {
// modify payload to RESTAdapter
var booksObj = widgetResults.books;
var booksArray = Object.keys(booksObj).map((element) => {
var book = booksObj[element];
book.id = book.book_id;
delete book.book_id;
return book;
});
console.log(booksArray);
this.store.pushPayload({books: booksArray});
});
return this.store.peekAll('book');
}
});
template
{{#each model as |book|}}
<article>
<h1>{{book.title}}</h1>
</article>
{{/each}}
There is straightforward solution to process your payload in model():
Define book model.
Process your payload in model() hook:
model() {
$.ajax({
url: endPoint,
type: 'GET',
dataType: 'jsonp'
}).then((widgetResults) => {
// modify payload to RESTAdapter
var booksObj = widgetResults.books;
var booksArray = Object.keys(booksObj).map((element) => {
var book = booksObj[element];
book.id = book.book_id;
delete book.book_id;
return book;
});
this.store.pushPayload({books: booksArray});
});
return this.store.peekAll('book');
}
Iterate model in controller or template as usual.
Working jsbin:
ember 1.13
ember 2.0
You want a custom serializer to translate the data from that format into JSON-API. JSON-API is an extremely well thought-out structure, so well in fact that ember-data has adopted it as the default format used internally. Some of the benefits are that it defines a structure for objects themselves, separating attributes from relationships; a means for embedding or including associated resources; defines a place for errors and other metadata.
In short, for whatever you're trying to do, JSON-API probably has already done a lot of the decision-making for you. And, by subclassing from DS.JSONSerializer, you'll be mapping right into the format that ember-data needs.
To do this, you create a custom adapter using ember generate serializer books:
// app/serializers/book.js
import DS from 'ember-data';
export default DS.JSONSerializer.extend({
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
// payload will contain your example object
// You should return a JSON-API document
const doc = {};
// ...
return doc;
}
});
For your example data, the output of the normalization should look something like this:
{
"data": [
{
"type": "books",
"id": 116429012,
"attributes": {
"title": "The Book of Three (The Chronicles of Prydain Book 1)",
"author_lf": "Alexander, Lloyd",
"author_fl": "Lloyd Alexander"
}
},
{
"type": "books",
"id": 1234,
"attributes": {
}
}
],
"meta": {
"settings": {
"amazonchoice":null,
"show":{
"showCovers":null,
"showAuthors":null
},
"style":null,
"domain":"www.librarything.com",
"textsnippets":{
"by":"by",
"Tagged":"Tagged","readreview":"read review","stars":"stars"
}
}
}
};
Then do
this.get('store').findAll('books').then((books) => {
const meta = books.get('meta');
console.log(meta.settings.domain);
books.forEach((book) => {
console.log(book.get('title'));
});
});
Code is not tested, but hopefully it gets you started.
Define settings and book models. Arrange for the API to respond to the endpoint /books returning data in the format:
{
settings: { ... },
books: [
{
id: xxx,
...
}
]
}
Retrieve the data in the model hook with this.store.findAll('book').
Iterate over the books in your template with {{#each model as |book|}}.