The techniques we use here is called Composition. We compose different objects and have them work together.
To start off, we have a json server with its initial content like so:
db.json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
{ "users": [ { "id": 1, "name": "Ricky", "age": 41 }, { "name": "Joy", "age": 41, "id": 2 }, { "name": "dexter", "age": 31, "id": 3 }, { "name": "Cao LuEn", "age": 2, "id": 4 } ] } |
Updating a User from the server
1 2 3 |
import { User } from './models/user' const user = new User({ id: 2 }); |
So we have a user. In order to update it from the latest data from the server, we’ll register an event ‘change’ and to pass a callback to let us know when it is done.
1) REGISTER event on
1 2 3 |
user.on('change', () => { console.log(user); }); |
Once our event has been saved, we call fetch which does two things:
- It fetches the data from server
- Once data arrives, we update it locally using Attributes (Object.assign)
- Then our Eventing will trigger ‘change’ to execute the callback
2) FETCH data from server
1 |
user.fetch(); |
where fetch is:
1 2 3 4 5 6 7 8 9 10 11 |
fetch(): void { const id = this.get('id'); if (typeof id !== 'number') { throw new Error('Cannot fetch without an id'); } this.sync.fetch(id).then( (response: AxiosResponse): void => { this.set(response.data); }) |
3) Set local properties
and set function is:
1 2 3 4 5 6 |
set(update: UserProps): void { // first we use Attributes to update the data locally that was passed in through constructor injection this.attributes.set(update); // then we trigger a change event to execute the callback of the 'change' event. this.events.trigger('change'); } |
4) Trigger event Change for completion
Saving user Data
First, we have the sync class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import axios, { AxiosPromise } from 'axios'; interface HasId { id?: number; } export class Sync<T extends HasId> { constructor(public rootUrl: string) {} fetch(id: number): AxiosPromise { return axios.get(`${this.rootUrl}/${id}`); } save(data: T): AxiosPromise { const { id } = data; if (id) { return axios.put(`${this.rootUrl}/${id}`, data); } else { return axios.post(this.rootUrl, data); } } } |
The save function says, we are passed in data that must have an optional id property.
If this id exists then we do a PUT request, which updates the user on the server.
If this id doesn’t exist, then we simply make a POST request, which creates the user on the server.
In our attributes class, implement a getAll function that returns all attributes as an object. This is naturally our dictionary this.data:
1 2 3 4 5 6 7 8 9 |
export class Attributes<T> { constructor(private data: T) {} ... ... // returns the data object, which houses all properties getAll(): T { return this.data; } } |
Now we are ready to implement save() for User.
Use use User’s sync property and call save.
That function looks to see if id exists in the Attributes object.
If id does exist, we update the user on the server by making a PUT request.
If id doesn’t exist, it creates a user on the server by making a GET request.
The save function return a Promise. We call then on it so when the async function completes, it will come to our callback. In the callback, we simply trigger the ‘save’ event to let it know that our operation has completed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
save(): void { this.sync .save(this.attributes.getAll()) .then( (response: AxiosResponse): void => { console.log('----- response from server for save() -----') console.log(response); this.trigger('save'); } ) .catch(() => { this.trigger('error'); }); } |
In index.tsx
To update User
1 2 3 4 5 |
const user = new User({ id: 2, name: 'Joy', age: 33 }) user.on('save', () => { console.log(user); }); user.save(); |
To create New User
1 2 3 4 5 |
const user = new User({ name: 'Cao Lu An', age: 0 }); user.on('save', () => { console.log(user); }); user.save(); |