Angular 6: Fetch Hash Table Data from JSON respond Backend - json

I have This JSON respond from my backend:
//User_Courses
[
{
id: 1,
name: "Ice King",
email: "pretty_princess1234#gmail.com"
completedCourses: [1,3],
unlockedCourses: [1,3,4,5,6],
completedLessons: [{"1" => [1,2,3]}, {"3" => [1,2,3,4,5,6,7]}, {"4" => [1]}]
},
{
id: 2,
name: "Mr. Crocker",
email: "fairy_godparents111#gmail.com"
completedCourses: [3],
unlockedCourses: [1,3,4],
completedLessons: [{"3" => [1,2,3,4,5,6,7]}, {"4" => [1,2]}]
}
]
// completed lessons are all the lesson the user finished.
// courses can be in progress or completed.
I want to fetch data from backend and subscribe it to this interface.
I don't sure how to implement the data structure and how to access data.
This is the interface I created:
export interface IUser {
id: number;
name: string;
email: string;
completedCourses: number[];
unlockedCourses: number[];
completedLessons: // <----- don't know what type to write
}
I want to know how to implement this, subscribe data with service and access data (in order to change it later and add data).
Thank you so much!

Create model for CompletedLesson (as mentioned in the comments):
interface ICompletedLesson {
[name: string]: number[];
}
interface IUser {
id: number;
name: string;
email: string;
completedCourses: number[];
unlockedCourses: number[];
completedLessons: ICompletedLesson[];
}
Then, create a service, something like this:
#Injectable()
export class UserService {
constructor(private http: HttpService) { }
fetchUserCourses(): Observable<IUser[]> {
return this.http.get<IUser[]>(`URL_TO_THE_USER_COURSES%);
}
}
And, wherever you are fetching data (some component for example):
fetchUserCourses() {
// userService is injected in this component's constructor
this.userService.fetchUserCourses().subscribe(users => {
// do something with result, yes, something like
this.users = users;
});
}
In the JSON you provided, to access the first lesson of the Mr. Crocker completed lessons (this.users are all users you retrieved from backend):
const firstCompletedLesson = this.users[1].completedLessons[0]; // {"3": [1,2,3,4,5,6,7]}
const lessons = firstCompletedLesson["3"]; // [1,2,3,4,5,6,7]
const firstLesson = lessons[0]; // 1
Furhermore, you can access "3" like this:
Object.keys(firstCompletedLesson)[0]; // 3
and you can add to array using push:
lessons.push(8); // [1,2,3,4,5,6,7,8]
and to add new completed lesson use:
this.users[1].completedLessons.push({ "5": [1, 2, 3] });
Hope this helps.

Related

Angular Map object from Get to Interface

I am getting JSON object from API.
In service I have:
getUserInfo(token: string): Observable<IUser> {
return this.http.get<any>(this.apiUrl.getUser, {headers: {'X-Auth-Token': token}}).pipe(
tap(data => console.log(data)),
catchError(this.handleError)
);
}
In component:
this.authenticationService.getUserInfo(this.token).subscribe({
next: result => {
this.user = result;
console.log(this.user);
},
error: err => console.log(err)
})
This is my interface (simplified):
export class IUser {
username: string;
email: string;
role: string;
numberOfUsers: number;
expirationDate: string;
}
Is there a way to automatically map JSON from http request to that interface, so:
If property does not exist in JSON object from request, set it to
default value, for example numberOfUsers=0 or expirationDate = null.
If there is extra property in JSON object from request, just ignore
it.
Currently the user gets overwritten with values from JSON object from request.
Is there any automatic function of Objectable that would do this? Or I have to write that method in interface?
what you describe is a class, you can't have default values in an interface.
check class-transformer, it does what you want: https://github.com/typestack/class-transformer
It can convert a plain object to a class instance and respect default values and unrelated fields:
export class IUser {
username: string;
email: string;
role: string;
numberOfUsers: number = 0;
expirationDate: string = null;
}
this.user = plainToClass(IUser, result, {
excludeExtraneousValues: true, // <- ignores keys not for the class.
});

TypeORM bulk create with relations

Is there a way to insert large amount of datas without blowing the JS heap memory ? I have a model which is Email as follow :
#Entity("email")
export class Email extends BaseEntity {
#PrimaryGeneratedColumn()
public id: number;
#ManyToOne((type) => Category, (cat) => cat.category, {nullable: false, cascade: ['insert']})
public category: Category;
#Column({type: "text", name: "email"})
public email: string;
}
and Category :
#Entity("category")
export class Category extends BaseEntity {
#PrimaryGeneratedColumn()
public id: number;
#Column({type: "text", name: "category"})
public category: string;
#OneToMany((type) => Email, (email) => email.category, {nullable: true})
public emails: Email[];
}
First problem I had is when I try to save {email: 'blabal#blalbah.com', category: 'default'} it says that Category must be an ID, but the thing is I want to add emails and create the category if it doesnt exist or asign the ID to the email if it exists. I did the following code :
public async bulkCreate(emails: Email[]): Promise<any> {
try {
const emailRepo = await getRepository(Email);
const categoryRepo = await getRepository(Category);
await Promise.all(emails.map(async (mail) => {
const cat = await categoryRepo.findOne({where: {category: mail.category}});
if (cat) {
// #ts-ignore
mail.category = cat.id;
} else {
const newCat = await categoryRepo.save(Object.assign(new Category(), mail));
// #ts-ignore
mail.category = newCat.id;
}
await emailRepo.save(mail);
}));
} catch (e) {
console.log(e);
throw new Error(e);
}
}
Worked for a few emails, but when I try to add even only 1,000 memory goes up to Like 4Gig and just crash.
What should I do? I'd like to add more than 1,000 emails at once.
I know it's little bit late, but solution for this use Bluebird Promise.map so you can define concurrency. instead executing in one run.

How to set json file to object's array in Angular

how to set json file to my "Client" object's array?
I have json, which looks like this:
Clients.json
{
"clients": [
{
"firstName": "nameA",
"lastName": "lastA",
"doctorsName": "domm",
"procedure": "consultation",
"registrationDate": "new Date(2019, 10, 12)",
"isAlreadyServed": "false"
},
{
"firstName": "nameB",
"lastName": "lastB",
"doctorsName": "domm",
"procedure": "procedureA",
"registrationDate": "new Date(2019, 10, 12)",
"isAlreadyServed": "false"
},
{
"firstName": "nameC",
"lastName": "lastC",
"doctorsName": "doctorA",
"procedure": "procedureC",
"registrationDate": "new Date(2019, 10, 12)",
"isAlreadyServed": "false"
},
...
...
...
And how can I set it to object's array Client.service.ts
clients: Client[] = this.http.get('path.Clients.json') // ??
Since your data is in the client-side as a JSON file. Though you can use HttpClient, here is another solution for the same.
You can directly import the JSON as a constant and assign it to clients as of TS 2.9 (docs).
import Clients from 'path/to/client/json';
clients: ClientsJson = Clients;
where the ClientsJson interface would look like this.
export interface ClientsJson {
clients: Client[];
}
export interface Client {
firstName: string;
lastName: string;
doctorsName: string;
procedure: string;
registrationDate: Date;
isAlreadyServed: boolean;
}
Here is a working example on StackBlitz.
You first need to define an interface that matches your object structure:
public interface ClientConfiguration {
firstName: string;
lastName: string;
doctorsName: string;
// And so on...
}
Then, just like the code you have shown, you need to type the http.get method in order to obtain the correct output.
public getConfiguration(): Observable<Array<ClientConfiguration>> {
return this.http.get<Array<ClientConfiguration>>('path/to/client/json');
}
Since my getConfiguration() method returns an observable, you will need to subscribe to it in your components or services:
#Component({ ... })
export class MyComponent implements OnInit {
public ngOnInit(): void {
this.getConfiguration().subscribe(result: Array<ClientConfiguration> => {
this.clientConfiguration = result;
});
}
public getConfiguration(): Observable<Array<ClientConfiguration>> {
return this.http.get<Array<ClientConfiguration>>('path/to/client/json');
}
}
You can read more here about HttpClient at Angular's official documentation: https://angular.io/guide/http
Hope it helps.

Typescript object from JSON

I'm using TypeScript to build an app and I'm making API calls to retrieve objects. For instance, I have a TypeScript User Object like this:
export class User {
id : number;
name : string;
email : string;
}
And my API returns
{
"id" : 3,
"name" : "Jonn",
"email" : "john#example.com"
}
I want to convert that JSON to a User. I've read in another posts I can do this:
let user : User = <User> myJson;
This seemly works. I can access properties of the user like user.namebut my problem is that, if the User class implements some method, the properties are not available. For example, if inside the User class I have this:
getUppercaseName() : string {
return this.name.toUppercase();
}
This happens:
user.name returns John but user.getUppercaseName() returns undefined
What's going on? How to solve this
What you are doing it treating classes as interfaces, as this will work exactly the same:
export interface User {
id : number;
name : string;
email : string;
}
The reason that the compiler doesn't complain about you using classes this way is because:
One of TypeScript’s core principles is that type-checking focuses on
the shape that values have. This is sometimes called “duck typing” or
“structural subtyping”
(read more about duck typing)
Or with an example:
class User {
id: number;
name: string;
email: string;
constructor(id: number, name: string, email: string) {
this.id = id;
this.name = name;
this.email = email;
}
}
function logUser(user: User) {
console.log(`user id: ${ user.id }, name: ${ user.name }, email: ${ user.email }`);
}
logUser({
id: 1,
name: "user 1",
email: "mailaddress"
});
logUser(new User(2, "user 2", "anotheraddress"));
In the two calls to logUser I pass objects that satisfy the interface of the User class.
If you want to have an instance of that class instead of an object that satisfies it then you should do something like:
new User(myJson.id, myJson.name, myJson.email);
And have a constructor like in my example, or:
interface IUser {
id: number;
name: string;
email: string;
}
class User implements IUser {
id: number;
name: string;
email: string;
constructor(data: IUser) {
this.id = data.id;
this.name = data.name;
this.email = data.email;
}
}
...
new User(myJson);
Nitzan pretty much explained the theory behind this, so I'll just provide an alternative approach:
interface UserInfo {
id: number;
name: string;
email: string;
}
class User {
userInfo: UserInfo;
constructor(userInfo: UserInfo) {
this.userInfo = userInfo;
}
getUpperCaseName(): string {
return this.userInfo.name.toLocaleUpperCase();
}
}
const json = {
id: 3,
name: "Jonn",
email: "john#example.com"
}
let user: User = new User(json);
There is a problem when your User has 50 or more properties...
Add a constructor in your User object so that it extends your json object.
export class User {
constructor( jsonUser: any )
{
$.extend(this, jsonUser);
}
id : number;
name : string;
email : string;
getUpperCaseName() {...}
}
In your ajax callback, create the User object from your json Object:
let receivedUser = new User( jsonUser );
let userName = receivedUser.getUpperCaseName();
I detailed the solution in that post.

Typescript create a class from json

I have an interface IPost and a class Post, I want to create Post from json data, in Post constructor I get the json response where the interface IPost matches it.
I mean IPost interface is generated by this great tool json2ts
json2ts : generate TypeScript interfaces from JSON
import { IPost, Title, Guid, Content, Excerpt, Embedded, Links } from './Ipost.ts';
export class Post implements IPost {
Id: number;
Date: string;
DateGmt: string;
Guid: Guid;
Modified: string;
ModifiedGmt: string;
Slug: string;
Type: string;
Link: string;
Title: Title;
Content: Content;
Excerpt: Excerpt;
Author: number;
FeaturedImage: number;
CommentStatus: string;
PingStatus: string;
Sticky: boolean;
Format: string;
Links: Links;
Embedded: Embedded;
constructor(json: any) {
var self = this;
json.subscribe(res => {
var jsonRes: any = res.json();
self = jsonRes; //something like this
});
}
}
Can I assign the class Post to json directly since json is described the same as Post class!
Is there any other ways than assigning each property to its peer from json?
Just as you would JavaScript you'll have to iterate over each value, and assign that value to self, using standard loop. Assigning to self in your case, simply changes the value of self to the json value, it doesn't make any changes this itself.
json.subscribe(res => {
let json = res.json();
for (var prop in obj) {
if( obj.hasOwnProperty( prop ) ) {
this[prop] = obj[prop];
}
}
});
NOTE: => binds this to the outer context (for example the class you're working with) to this, so that you do not need to the intermediate self variable.