Angular 4+ handling multiarray json response best practice - json

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!

Related

Sequelize - include column and getting string instead of object

I have an problem which I can't resolve in any way, I'm including user -> avatar models in PostTip model and when I want to add column with OBJECT avatar (id + src) I can't make it.
Name, email works because I'm pointing to single value not object.
Where is the problem?
PostTip.findAndCountAll({
attributes: {
include: [
** [db.sequelize.col(`"user"->"avatar"`), "avatar"],**
[db.sequelize.col(`"user"."name"`), "name"],
[db.sequelize.col(`"user"."email"`), "email"],
],
},
offset: offset,
limit: limit,
order: order,
where: {
postId: req.params.id,
},
include: [
{
model: User,
as: "user",
attributes: [],
include: [{ model: AwsAvatar, as: "avatar" }],
},
],
}).then((result) => {
console.log(JSON.stringify(result.rows, null, 2));
const data = getPagingData(result.rows, result.count, query.page, limit);
return res.send(data);
});
};
and results are:
{
"id": 1,
"userId": 1,
"postId": 1,
"tipAmount": 100,
"createdAt": "2023-01-11T22:10:26.440Z",
"updatedAt": "2023-01-11T22:10:26.440Z",
** "avatar": "(1,1,https://dh7ieyc6s2dxm.cloudfront.net/avatars/1673514260216_0e69489d-fc95-4ed8-b615-2a76ce43385.webp,\"2023-01-12 09:04:21.223+00\",\"2023-01-12 09:04:21.223+00\")",**
"name": "Test User",
"email": "test#test"
}
Im expecting to have output like that:
{
"id": 1,
"userId": 1,
"postId": 1,
"tipAmount": 100,
"createdAt": "2023-01-11T22:10:26.440Z",
"updatedAt": "2023-01-11T22:10:26.440Z",
** "avatar": {
"id": 1,
"userId": 1,
"src": "https://dh7ieyc6s2dxm.cloudfront.net/avatars/1673514260216_0e69489d-fc95-4ed8-b615-2a76ce5ff385.webp",
"createdAt": "2023-01-12T09:04:21.223Z",
"updatedAt": "2023-01-12T09:04:21.223Z"
},**
"name": "Test User",
"email": "test#test"
}
User model:
import { Optional } from "sequelize";
import {
Column,
Table,
Model,
DataType,
ForeignKey,
BelongsTo,
Default,
HasMany,
HasOne,
} from "sequelize-typescript";
import UserRole from "../../enums/user-role.enum";
import AwsAvatar from "../aws/aws-avatar.model";
import PostComment from "../post/post-comment.model";
import PostLike from "../post/post-like.model";
import PostTip from "../post/post-tip.model";
import Card from "./card.model";
import Role from "./role.model";
interface UserAttributes {
id: number;
avatar: AwsAvatar;
name: string;
setName: string;
description: string;
hashtag: string;
email: string;
password: string;
isVip: boolean;
lastUsedCardNum: string;
sendNotifications: boolean;
cards: Card[];
}
interface UserCreationAttributes
extends Optional<
UserAttributes,
| "id"
| "avatar"
| "setName"
| "description"
| "hashtag"
| "cards"
| "isVip"
| "lastUsedCardNum"
| "sendNotifications"
> {}
#Table({ tableName: "users" })
export default class User extends Model<
UserAttributes,
UserCreationAttributes
> {
#Column(DataType.STRING)
public name!: string;
#Column(DataType.STRING)
public setName!: string;
#Column(DataType.STRING)
public email!: string;
#Column(DataType.STRING)
public password!: string;
#HasOne(() => AwsAvatar)
public avatar!: AwsAvatar;
#Column(DataType.STRING)
public description!: string;
#Column(DataType.STRING)
public hashtag!: string;
#Default(false)
#Column(DataType.BOOLEAN)
public isVip!: boolean;
#Default(false)
#Column(DataType.BOOLEAN)
public subscriptionActive!: boolean;
#Column(DataType.DATE)
public subscriptionExpireDate!: Date;
#Default(false)
#Column(DataType.BOOLEAN)
public emailConfirmed!: boolean;
#Default(false)
#Column(DataType.BOOLEAN)
public invalidEmail!: boolean;
#Column
public buyedContent!: string;
#Default(0)
#Column(DataType.INTEGER)
public wallet!: number;
#Default(0)
#Column(DataType.INTEGER)
public spent!: number;
#Default(false)
#Column(DataType.BOOLEAN)
public isMessageReaded!: boolean;
#Default(false)
#Column(DataType.BOOLEAN)
public sendNotifications!: boolean;
//
#Column
public messageContainerDto!: string;
#Column(DataType.STRING)
public lastUsedCardNum!: string;
#HasMany(() => Card)
public cards!: Card[];
#ForeignKey(() => Role)
#Default(UserRole.User)
#Column(DataType.INTEGER)
public roleId!: number;
#BelongsTo(() => Role)
public role!: Role;
#HasMany(() => PostTip)
public tips!: PostTip[];
#HasMany(() => PostComment)
public comments!: PostComment[];
#HasMany(() => PostLike)
public likes!: PostLike[];
}
Avatar model:
import { Optional } from "sequelize";
import {
Column,
Table,
Model,
DataType,
ForeignKey,
BelongsTo,
} from "sequelize-typescript";
import User from "../user/user.model";
interface AwsAvatarAttributes {
id: number;
src: string;
}
interface AwsAvatarCreationAttributes
extends Optional<AwsAvatarAttributes, "id"> {}
#Table({ tableName: "aws_avatars" })
export default class AwsAvatar extends Model<
AwsAvatarAttributes,
AwsAvatarCreationAttributes
> {
#ForeignKey(() => User)
#Column(DataType.INTEGER)
public userId!: number;
#BelongsTo(() => User, { onDelete: "CASCADE" })
public user!: User;
#Column(DataType.STRING)
public src!: string;
}
I know it can be done smth like that, but cmon, I believe that there is any way to join avatar table and not for each loop every time when fetching data:
private getPostTips = (req: Request, res: Response, next: NextFunction) => {
const query = req.query as unknown as { name: string } & PaginationDto;
const { limit, offset } = getPagination(query.page, query.size);
const condition = query.name
? {
name: {
[Op.iLike]: "%" + query.name + "%",
},
}
: {};
const order: any[string] = [[query.sort, query.order]];
PostTip.findAndCountAll({
raw: true,
nest: true,
attributes: {
include: [
[db.sequelize.col(`"user"."name"`), "name"],
[db.sequelize.col(`"user"."email"`), "email"],
],
},
offset: offset,
limit: limit,
order: order,
where: { postId: req.params.id },
include: [
{
model: User,
as: "user",
attributes: [],
where: condition,
include: [{ model: AwsAvatar, as: "avatar" }],
},
],
}).then((result) => {
**const sorted: any[] = [];
for (let i = 0; i < result.rows.length; i++) {
let item = {
avatar: result.rows[i].user.avatar,
...result.rows[i],
user: undefined,
};
sorted.push(item);
}**
const data = getPagingData(sorted, result.count, query.page, limit);
return res.send(data);
});
};

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
}

Mapping between two different Angular models

I'm trying to map Order.ts to OrderAction.ts, but getting the error Cannot ready property '0' of undefined. This error is coming up when I try to map the Order properties to OrderAction properties.
Here is Order.ts:
export class Order {
OrderId: number;
FunctionStatusList?: OrderFunctionStatus[];
}
export class OrderFunctionStatus {
FunctionTypeCode: number;
AvailableActions: OrderAvailableAction[];
}
export class OrderAvailableAction {
ActionLabel: string;
ActionValue: string;
}
Here is OrderAction.ts:
export class OrderAction {
FunctionTypeCode: number;
SelectedAction: string;
OrderList: AvailableAction[];
}
export class AvailableAction {
OrderId: number;
IsAvailableAction: boolean;
AvailableActions?: OrderAvailableAction[];
}
Here is the code that I wrote:
orders: any[] = [];
orderActionList: any[] = [];
ngOnInit() {
this.orders = this.orderService.getCheckedOrders();
this.orders.forEach((order: Order, i) => {
let orderAction: OrderAction = new OrderAction();
orderAction.OrderList[i].OrderId = order.OrderId;
orderAction.FunctionTypeCode = order.FunctionStatusList[i].FunctionTypeCode;
orderAction.AvailableActions = order.FunctionStatusList[i].AvailableActions;
orderAction.IsAvailableAction = order.FunctionStatusList[i].AvailableActions.length > 0 == true || false;
this.orderActionList.push(orderAction);
});
}
Here is a sample of the Order.ts json:
{
"OrderId": "1",
"FunctionStatusList": [{
"FunctionTypeCode": "1",
"AvailableActions": [{
"ActionLabel": "1",
"ActionValue": "1"
}]
}]
}
Here is a sample of the OrderAction.ts json:
{
"FunctionTypeCode": "1",
"SelectedAction: "1",
"OrderList": [{
"OrderId": "1",
"IsAvailableActionsLoaded": "1",
"AvailableActions": [{
"ActionLabel": "1",
"ActionValue": "1"
}]
}]
}
I'm not sure where exactly you're getting the error, but I did the following and it converts Order to OrderAction: https://plnkr.co/edit/VEHAdk3qPRIFkEAU?preview
The meat of the code is this:
this.orders.forEach((order: Order, i) => {
this.orderActions.push({
FunctionTypeCode: order.FunctionStatusList[i].FunctionTypeCode,
SelectedAction: null,
OrderList: [
{
OrderId: order.OrderId,
IsAvailableActionsLoaded:
order.FunctionStatusList[i].AvailableActions.length > 0,
AvailableActions: order.FunctionStatusList[i].AvailableActions,
},
],
});
});
I left SelectedAction as null because it was unclear how this value would be set.

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