Typescript JSON to interface dynamically - json

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
}

Related

Angular 4+ handling multiarray json response best practice

user.ts
import { Company } from "./company";
export class User{
token: string;
companies: Company;
name: string;
email: string;
currentCompanyID: string;
constructor(){
}
}
company.ts
export class Company{
companyId: string;
name: string;
orgNo: string;
constructor(){
}
}
service.ts
getData(): Observable<User> {
return this.http.get(this.url).map((res: Response) => res.json())
}
component.ts
//call to my api in service.ts
this.avrs.getData().subscribe(
res => {
console.log("Result");
console.log("main res: " + res);
console.log("Name: " + res.name);
console.log("companyid: " + res.currentCompanyID);
console.log("companies: " + res.companies);
console.log("token: " + res.token);
console.log("user data: " + res.companies.companyId);
console.log("user data: " + res.companies.name);
console.log("user data: " + res.companies.orgNo);
},
error => {
console.log(error);
},
() => {
}
);
}
Output
Result
main res: [object Object]
Name: undefined
companyid: 28764
companies: [object Object]
token: lkjdfjsgosdfjuguerujgoiehjiughdskjge9r8w
user data: undefined
user data: undefined
user data: undefined
Json Response example:
{
"user": {
"id": 123456,
"company_id": "28764",
"name": "TEST",
"email": "test#dummy.com",
"mobile": "91273493412412",
"locale": "en",
"companies": [
{
"companyId": "idww",
"name": "nameww",
"orgNo": "orgww",
"roles": [
{
"role": "Admin"
},
{
"role": "Guest"
}
],
"services": []
},
{
"companyId": "idqq",
"name": "nameqq",
"orgNo": "orgqq",
"roles": [
{
"role": "Admin"
},
{
"role": "Guest"
}
],
"services": []
},
],
},
"token": "lkjdfjsgosdfjuguerujgoiehjiughdskjge9r8w",
"currentCompanyID": "28764"
}
I want to get:
token,
companyid,
user -> name,
user -> companies array
The question is basically what is the best way (best practice) to handle many array in a json file in Angular 2+? As you can see with my output I'm only getting companyid and token data, and not getting companies array or user data.
Create Role Model:
export class Role{
id: number;
name: string;
constructor(attrs: any = null) {
if (attrs) {
this.build(attrs);
}
}
build(attrs: any): void {
this.id = attrs.id;
this.name = attrs.name;
}
}
Create Company Model:
export class Company{
id: number;
name: string;
roles: Array<Role>;
services: any;
constructor(attrs: any = null) {
if (attrs) {
this.build(attrs);
}
}
build(attrs: any): void {
this.id = attrs.id;
this.name = attrs.name;
this.services = attrs.services;
if(attrs.roles) {
this.roles = attrs.roles.map(
r => new Role(r)
);
}
}
}
Create User Model:
export class User{
id: number;
name: string;
email: string;
companies: Array<Company>;
constructor(attrs: any = null) {
if (attrs) {
this.build(attrs);
}
}
build(attrs: any): void {
this.id = attrs.id;
this.name = attrs.name;
this.email = attrs.email;
if(attrs.companies) {
this.companies = attrs.companies.map(
c => new Company(c)
);
}
}
}
By doing so, you create entities automatically. According to your Json Response example, you need to create another model. Let's call it Data!
export class Data{
user: User;
token: string;
currentCompanyID: string;
constructor(attrs: any = null) {
if (attrs) {
this.build(attrs);
}
}
build(attrs: any): void {
this.currentCompanyID = attrs.currentCompanyID;
this.token = attrs.token;
this.user = attrs.user;
}
}
Retrieve data from sample service method like this:
getData(): Observable<any>{
return this.http
.get('awesome_url')
.map((data: any) => new Data(data));
}
Hope I help!

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;
}

Supplied parameters do not match any signature of call target on api call angular4

I am consuming an api to Covalent UI, on user service. Which needs to post some data from an endpoint to the table as illustrated on the example from the GitHub.
Here is the modification I have made to the service.
import { Provider, SkipSelf, Optional, InjectionToken } from '#angular/core';
import { Response, Http } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { HttpInterceptorService, RESTService } from '#covalent/http';
import { ApiService } from '../../../../services/api.service';
import { AuthService } from '../../../../services/auth.service';
export interface IUser {
_id: string;
email:string;
createdAt: Date;
profile: {
name: string;
gender: string;
location: String;
picture: {
// data: Buffer;
contentType: string;
}
}
}
export class UserService extends RESTService<IUser> {
constructor(private _http: HttpInterceptorService, api: string,
private authService: AuthService,
private api2: ApiService,) {
super(_http, {
baseUrl: api,
path: '/dashboard/users',
});
}
staticQuery(): Observable<IUser[]> {
// return this._http.get('data/users.json')
// .map((res: Response) => {
// return res.json();
// });
return this.api2.get('auth/account/users')
.map((res: Response) => {
return res.json();
});
}
}
export const USERS_API: InjectionToken<string> = new InjectionToken<string>('USERS_API');
export function USER_PROVIDER_FACTORY(
parent: UserService, interceptorHttp: HttpInterceptorService, api: string): UserService {
return parent || new UserService(interceptorHttp, api);//<---- This is where I get the error mention.
}
export const USER_PROVIDER: Provider = {
// If there is already a service available, use that. Otherwise, provide a new one.
provide: UserService,
deps: [[new Optional(), new SkipSelf(), UserService], HttpInterceptorService, USERS_API],
useFactory: USER_PROVIDER_FACTORY,
};
JSON api data
[
{
"_id": "59d665c3acbde702b47d3987",
"updatedAt": "2017-10-07T17:23:00.498Z",
"createdAt": "2017-10-05T17:02:59.526Z",
"email": "me#mail.com",
"password": "$2a$05$z1mRUWqqUfM8wKMU/y9/sOLssAKcV7ydxi0XJyTR1d3BI2X7SSsoy",
"tokens": [],
"role": "admin",
"__v": 0,
"profile": {
"name": "F.name L.name",
"gender": "Female",
"location": "my place",
"avatar": {
"contentType": "image/png",
"data": "iVBORw0KGgoAAAANSUhEUgAAAaYAAAFmCAYAAAAmm....."
}
}
}
]
Am not sure what am doing wrong, I will appreciate your comment for this fix.
I get the error bellow.
users/services/user.service.ts (51,20): Supplied parameters do not match any signature of call target.
From this line of code
As #Philipp mentioned in the comments.
The class UserService expects 4 arguments in the constructor, but you are only providing 2 in the USER_PROVIDER_FACTORY function.
Therefore your factory should be defined:
export function USER_PROVIDER_FACTORY(
parent: UserService, interceptorHttp: HttpInterceptorService, api: string,
authService: AuthService, api2: ApiService
): UserService {
return parent || new UserService(interceptorHttp, api, authService, api2)
}

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 parsing from JSON to object

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