Angular2 parsing from JSON to object - json

I'm trying to find the best way to cast my json object to Typescript object.
I have a http get service which returns a list of user.
My current version works, I have added from JSON function to all my model classes to make the mapping works:
export class User {
constructor(
public pk: number,
public username: string,
public first_name: string,
public last_name: string,
public email: string,
public profile: UserProfile, ) {
}
static fromJSON(json: any): User {
let user = Object.create(User.prototype);
Object.assign(user, json);
user.profile = UserProfile.fromJSON(json.profile);
return user;
}
}
That works well. But there is something I don't get in the angular 2 doc. On the heroes tutorial, the JSON is automatically casted to object this way:
getHeroes (): Observable<Hero[]> {
return this.http.get(this.heroesUrl)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
let body = res.json();
return body.data || { };
}
I can't get this method to work on my case, I says that body.data is undefined.
Does this method really works?
EDIT:
My http service doesn't returns an array of users. It returns a page which contains an array of users in its 'results' property.
{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"pk": 48,
"first_name": "Jon",
"last_name": "Does",
"profile": {
"pk": 46,
"gender": "U"
}
},
{
"pk": 47,
"first_name": "Pablo",
"last_name": "Escobar",
"profile": {
"pk": 45,
"gender": "M"
}
}
]
}
My service code:
private extractData(res: Response) {
let body = res.json().results;
return body || {}; //<--- not wrapped with data
}
search(authUser: AuthUser, terms: string): Observable<User[]> {
let headers = new Headers({
'Content-Type': 'application/json',
'X-CSRFToken': this.cookiesService.csrftoken,
'Authorization': `Token ${authUser.token}`
});
let options = new RequestOptions({ headers: headers });
return this.http.get(environment.server_url + 'user/?search=' + terms, options)
.map(this.extractData);
// .map((response: Response) => response.json());
}
My search component code:
onSearch(terms: string) {
this.searchService.search(this.user, terms).subscribe(
response => {
console.log(response); // Return array of object instead of array of user
},
error => {
console.log(JSON.stringify(error));
},
() => { }
);
}
EDIT 2:
To make this case easier, I've wrote this simple code:
test(){
let json_text=` [
{
"id": 1,
"text": "Jon Doe"
},
{
"id": 1,
"text": "Pablo Escobar"
}
]`;
console.log(<MyObject[]>JSON.parse(json_text)); // Array of objects
console.log(MyObject.fromJSON(JSON.parse(json_text))); // Array of 'MyObject'
}
export class MyObject{
id: number;
text: string;
static fromJSON(json: any): MyObject {
let object = Object.create(MyObject.prototype);
Object.assign(object, json);
return object;
}
}
console.log(<MyObject[]>JSON.parse(json_text)) returns a list of Objects
console.log(MyObject.fromJSON(JSON.parse(json_text))) returns a
list of MyObject

It's because in Angular tutorial, json is in the data property.
As stated in the tutorial
Make no assumptions about the server API. Not all servers return an
object with a data property.
If you are not wrapping your json with any property you can just use
private extractData(res: Response) {
let body = res.json();
return body || { }; //<--- not wrapped with data
}
Update:
Component code
onSearch(terms: string) {
this.searchService.search(this.user, terms).subscribe(
(response: SearchResponse) => { // <--- cast here
console.log(response);
},
error => {
console.log(JSON.stringify(error));
},
() => { }
);
}

I am quite late to this topic but found my self into the same issue . I am learning Angular and want to convert JSON received from HTTP server to my model object .
Service Class
var ele:User;
let k=this.http.get<User>(url).subscribe(data => {
ele=data;
console.log(ele.count);
console.log(ele.results[0].first_name);
console.log(ele.results[0].profile.gender);
}
);
My Model for holding the information of JSON
export interface User{
count: string;
next: string;
previous: string;
results: Result[];
}
export interface Result{
pk: string;
first_name: string;
last_name: string;
profile:Profile;
}
export interface Profile{
pk: string;
gender:string;
}
And this is it. I am using Angular 6 for parsing JSON to Object

Related

Typescript JSON to interface dynamically

I'm pretty new to typescript and I want to turn the below JSON into an interface/type but the user1 key is dynamic and could be something different but the JSON inside the of key will be the same.
{
"code": 200,
"status": "success",
"data": {
"user1": {
"firstName": "John",
"lastName": "Smith",
"age": 25
}
}
}
I have the below so far. Is it possible to turn the data into a map in the Root interface as this is how I would do it in golang.
export interface Root {
code: number
status: string
data: Data
}
export interface Data {
[key: string]: User
}
export interface User {
firstName: string
lastName: string
age: number
}
export const sendRequest = (url: string): Root => {
const [data,setData]=useState([]);
const getData=()=>{
fetch(url
,{
headers : {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
}
)
.then(function(response){
return response.json();
})
.then(function(myJson) {
setData(myJson)
});
}
useEffect(()=>{
getData()
},[])
return JSON.parse(JSON.stringify(data));
}
const user = sendRequest(host + path)
console.log(user.data?.[0])
You can use index signatures:
export interface Root {
code: number
status: string
data: Data
}
export interface Data {
[key: string]: User
}
export interface User {
firstName: string
lastName: string
age: number
}

Angular 5 Observable mapping to Json array

My backend return this :
{
"FirstResponse": [
{
"MyField1": "AAA",
"MyField2": "AAAAAAA"
},
{
"MyField1": "BBB",
"MyField2": "BBBBBBB"
},
{
"MyField1": "CCC",
"MyField2": "CCCCC"
}
],
"SecondResponse": [
{
"FirstName": "FirstNameA",
"LastName": "LastNameA"
},
{
"FirstName": "FirstNameB",
"LastName": "LastNameB"
}
]
}
I'd like map FirstReponse to a variable and SecondResponse to another variable.
How can I adapt the code below ?
search(): Observable<any> {
let apiURL = `......`;
return this.http.get(apiURL)
.map(res => res.json())
}
Update : Excepted result
In one variable this :
[
{
"MyField1": "AAA",
"MyField2": "AAAAAAA"
},
{
"MyField1": "BBB",
"MyField2": "BBBBBBB"
},
{
"MyField1": "CCC",
"MyField2": "CCCCC"
}
]
In a second :
[
{
"FirstName": "FirstNameA",
"LastName": "LastNameA"
},
{
"FirstName": "FirstNameB",
"LastName": "LastNameB"
}
]
You could create a new file which exports the model class and then assign it to the returning Observable type. Something like:
new model.ts file
class FieldModel {
Field1: string;
Field1: string;
}
export class valuesModel {
MyValues: Array<FieldModel>;
}
on the service.ts
import { valuesModel } from 'model';
search(): Observable<valuesModel> {
let apiURL = `https://jsonplaceholder.typicode.com/users`;
return this.http.get(apiURL)
.map(res => res.json())
}
Please check this approach, use
import { Http, Response} from '#angular/http';
import { Observable } from 'rxjs/Observable';
public search(){
let apiURL = `https://jsonplaceholder.typicode.com/users`;
return this.http.get(apiURL)
.map((res: Response)=> return res.json();)
.catch((error: Response) => {
return Observable.throw('Something went wrong');
});
}
for this search() method you can subscribe from your component.
And if you want to map output into respected modal then please provide format of same.So that i can help
I don't crealry understan what you wanna get because you not provide example result,
however try this - change line:
.map(res => res.json())
to
.map(res => res.json().MyValues )
using this you will get at the top level similar array like in link you provided in comment below you question: https://jsonplaceholder.typicode.com/users
UPDATE (after question update 9.10.2018)
Currently .map(res => res.json()) returns object that has two fields (variables) "FirstResponse" and "SecondResponse". You can have acces to it by for example (I write code from head):
public async loadData()
{
let data = await this.yourService.search().toPromise();
let firstVariable = data.FirstResponse;
let secondVariable = data.SecondResponse;
...
}
So as you describe in your question/comments in loadData() you get result in two variables as you want.
Or alternative answer - if you wanna do this inside search() then you can do that in such way for example:
search(): Observable<any> {
let apiURL = `......`;
return this.http.get(apiURL)
.map( (res) => {
let data = res.json();
return {
firstVariable: data.FirstResponse,
secondVariable: data.SecondResponse,
}
})
}

How to map JSON response to Model in Angular 4

I have tried a lot but I am not able to get endpoint response mapped to my model. I am using HttpClient and Angular4.
I got data back from service but it is not mapped correctly to my model class.
I have following JSON which service is Returning:
{
"result": [
{
"id": "1",
"type": "User",
"contactinfo": {
"zipcode": 1111,
"name": "username"
}
}
]
}
I have created a model in typescript which I will like to map to json response:
export interface result {
zipcode: number;
name: string;
}
This is how i call JSON endpoint.
result : string[] = [];
constructor(private http: HttpClient) { }
public getList(): result[] {
this.http.get<result[]>('url...', { headers: this.headers }).subscribe(
data => {
// 1. Data is returned - Working
console.log('data: ' + data);
this.result= data;
// This is the problem. My data is not mapped to my model. If I do following a null is returned
console.log('data mapped: ' + this.result[0].name);
},
(err: HttpErrorResponse) => {
// log error
}
);
return this.result;
}
You need to import the interface in your component,
import { result } from '.result';
Your interface should look like,
interface RootObject {
result: Result[];
}
interface Result {
id: string;
type: string;
contactinfo: Contactinfo;
}
interface Contactinfo {
zipcode: number;
name: string;
}
and change the type of result as,
result : result;
and assign the result as,
this.result = data;
You can use http://www.jsontots.com/ to create the interface based on JSON
your "data" is an Object, with a property "result". result[] has a property called "contactInfo". in contactInfo you have the data you want, so
//you have no model, so NOT get<result[]>
this.http.get('url...', { headers: this.headers }).subscribe(
data => {
this.result= data.result[0].contactInfo;
}

How to pass dynamic attribute/parameter to openDialog?

I need to pass the following ID: 59dc921ffedff606449abef5 dynamically to MatDialog. For testing proposes I'am using it as hard coded ID.
Unfortunately all my searches and tries failed and I can't get the id dynamically into the function call. I tried also the #input feature, but it didn't help.
edit-dilog.component.ts:
export class EditDialogComponent implements OnInit {
dialogResult:string = '';
constructor(public dialog:MatDialog, public loginService:LoginService ){ }
ngOnInit() {}
openDialog() {
this.dialog.open(EditUserComponent, { data: '59dc921ffedff606449abef5' })
.afterClosed()
.subscribe(result => this.dialogResult = result);
}
}
edit-user.component.ts:
export class EditUserComponent implements OnInit {
public message:any [];
public resData: {};
constructor(public thisDialogRef: MatDialogRef<EditUserComponent>,
#Inject(MAT_DIALOG_DATA) public data: number,
public loginService: LoginService) { }
ngOnInit() {
this.loginService.getSingleUser(this.data)
.subscribe(data => {
this.resData = JSON.stringify(data);
})
}
onCloseConfirm() {
this.thisDialogRef.close('Confirm');
}
onCloseCancel() {
this.thisDialogRef.close('Cancel');
}
}
The ID is coming from JSON Response in a service login-service.ts:
getSingleUser(id) {
return this.http.get(environment.urlSingleUsers + '/' + id, this.options)
.map(res => {
console.log('RES: ' + JSON.stringify( res.json() ) );
return res.json();
}).catch( ( error: any) => Observable.throw(error.json().error || 'Server error') );
}
extractData(result:Response):DialogUserData[] {
return result.json().message.map(issue => {
return {
ID: issue._id,
Email: issue.email,
Name: issue.fullName
}
});
}
And here is where I do the call of openDialog():
<i class="material-icons" (click)="openDialog()">create</i>
For more clarification here is how the JSON Response comes:
"message": [
{
"_id": "59415f148911240fc812d393",
"email": "jane.doe#foo.de",
"fullName": "Jane Doe",
"__v": 0,
"created": "2017-06-14T16:06:44.457Z"
},
{
"_id": "5943b80be8b8b605686a67fb",
"email": "john.doe#foo.de",
"fullName": "John Doe",
"__v": 0,
"created": "2017-06-16T10:50:51.180Z"
}
]
I just did something similar, though I'm a little bit confused by how you name the components (seems should be the other way around).
You can try: fetch the data (user) first and then (actually) open the dialog in your controlling component:
edit-dialog.component.ts:
openDialog(id: string) {
this.loginService.getSingleUser(id)
.subscribe(user=> {
const dialogRef = this.dialog.open(EditUserComponent, {
data: user
});
dialogRef.afterClosed().subscribe(result => {
console.log(`Dialog result: ${result}`);
});
});
}
You can then access the dialog data (user) to render the dialog view:
edit-user.component.ts:
ngOnInit() {
console.log(this.data);
}
In this way, you can pass the id dynamically:
<i class="material-icons" (click)="openDialog(id)">create</i>
where the id can be a member of your controlling component.

Angular2 mapping of nested JSON arrays to model

I am trying to map the received JSON-data on my created Models. The problem is, that the JSON-data has nested arrays. So it is not possible to map my data with the way I am trying to. Is there a mistake in my way or is there a better way to map my JSON-data ?
JSON-Data
{
"data": {
"apiName": "test-application",
"stages": [
{
"stage": "prod",
"id": "xxxxxxxx",
"methods": [
{
"id": "xxxxxx",
"path": "/users/create",
"httpMethods": [
"GET"
],
"methodName": "testMethod",
"url": "https://xxxx/xxxxx/xxxxxx"
}
]
},
{
"stage": "dev",
"id": "xxxxxxx",
"methods": [
{
"id": "xxxxxxx",
"path": "/users/create",
"httpMethods": [
"GET"
],
"methodName": "testMethod",
"url": "https://xxxxx/xxxxxx/xxxx"
}
]
}
]
}
}
Models:
import {ApiStage} from "./ApiStage";
export class Api {
constructor(values: Object = {}){
Object.assign(this, values);
}
public apiName: string;
public stages: ApiStage[];
}
import {ApiMethod} from "./ApiMethod";
export class ApiStage {
constructor(values: Object = {}){
Object.assign(this, values);
}
public stage: string;
public id: string;
public methods: ApiMethod[];
}
export class ApiMethod {
constructor(values: Object = {}){
Object.assign(this, values);
}
public id: string;
public path: string;
public httpMethods: string[];
public methodName: string;
public url: string;
}
HTTP-method in service:
getApi() {
return this.http.post(this.url, this.data, {headers: this.headers})
.map(this.extractData)
.map(api => new Api(api))
.catch((error: any) => Observable.of(error.json().error || 'Server error'));
}
private extractData(res: Response) {
let body = res.json();
return body.data || {};
}
JSON has just a very limited set of data types - string, number, boolean, array, object. If you want to convert a JSON object tree to a tree of objects of your custom classes, it's necessary to do it recursively and with creating correct objects - not working with objects that just look like being of your classes.
This process can be tedious and error prone, so it's better to use a library such as Class transformer (https://github.com/pleerock/class-transformer) which can do it for you. You just annotate your classes with decorators (such as #Type(...)) and then you can transform plain JSON objects using plainToClass() method or serialize real objects to JSON using classToPlain().