Skip to content

Commit 46aa23f

Browse files
committed
Implement persistence with the save() method
1 parent e721448 commit 46aa23f

File tree

3 files changed

+82
-42
lines changed

3 files changed

+82
-42
lines changed

README.MD

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ A lightweight Angular 2 adapter for [JSON API](http://jsonapi.org/)
1111
- [Finding Records](#finding-records)
1212
- [Querying for Multiple Records](#querying-for-multiple-records)
1313
- [Retrieving a Single Record](#retrieving-a-single-record)
14-
- [Creating Records](#creating-records)
14+
- [Creating, Updating and Deleting](#creating-updating-and-deleting)
15+
- [Creating Records](#creating-records)
16+
- [Persisting Records](#persisting-records)
17+
- [Relationships](#relationships)
1518
- [Custom Headers](#custom-headers)
1619
- [TODO](#todo)
1720
- [Development](#development)
@@ -216,7 +219,9 @@ this.datastore.findRecord(Post, '1', {
216219
);
217220
```
218221

219-
### Creating Records
222+
### Creating, Updating and Deleting
223+
224+
#### Creating Records
220225

221226
You can create records by calling the `createRecord()` method on the datastore:
222227
- The first argument is the type of object you want to create.
@@ -226,39 +231,53 @@ You can create records by calling the `createRecord()` method on the datastore:
226231
this.datastore.createRecord(Post, {
227232
title: 'My post',
228233
content: 'My content'
229-
}).subscribe(
234+
});
235+
```
236+
237+
#### Persisting Records
238+
239+
Records are persisted on a per-instance basis. Call `save()` on any instance of `JsonApiModel` and it will make a network request.
240+
241+
```typescript
242+
let post = this.datastore.createRecord(Post, {
243+
title: 'My post',
244+
content: 'My content'
245+
});
246+
247+
post.save();
248+
```
249+
250+
The `save()` method will return an `Observer` that you can subscribe:
251+
252+
```typescript
253+
post.save().subscribe(
230254
(post: Post) => console.log(post)
231255
);
232256
```
233257

258+
#### Relationships
259+
234260
If the object you want to create has a **one-to-many** relationship, you can do this:
235261

236262
```typescript
237-
this.datastore.createRecord(Comment, {
263+
let comment = this.datastore.createRecord(Comment, {
238264
title: 'My comment',
239265
post: post
240-
}).subscribe(
241-
(comment: Comment) => console.log(comment)
242-
);
266+
});
243267
```
244268

245269
where `post` is an object of type `Post`, previously retrieved from the API.
246270

247-
If you want to include a relationship when creating a record to have it parsed in the response, you can pass the `params` object:
271+
If you want to include a relationship when creating a record to have it parsed in the response, you can pass the `params` object to the `save()` method:
248272

249273
```typescript
250-
this.datastore.createRecord(Comment, {
251-
title: 'My comment',
252-
post: post
253-
}, {
274+
comment.save({
254275
include: 'user'
255276
}).subscribe(
256277
(comment: Comment) => console.log(comment)
257278
);
258279
```
259280

260-
261-
262281
### Custom Headers
263282

264283
By default, the library adds these headers, according to the [JSON API MIME Types](http://jsonapi.org/#mime-types):
@@ -277,12 +296,17 @@ this.datastore.headers = new Headers({'Authorization': 'Bearer ' + accessToken})
277296
Or you can pass the headers as last argument of any datastore call method:
278297

279298
```typescript
280-
this.datastore.createRecord(Post, {
281-
title: 'My post',
282-
content: 'My content'
299+
this.datastore.query(Post, {
300+
include: 'comments'
283301
}, new Headers({'Authorization': 'Bearer ' + accessToken}));
284302
```
285303

304+
and in the `save()` method:
305+
306+
```typescript
307+
post.save({}, new Headers({'Authorization': 'Bearer ' + accessToken}));
308+
```
309+
286310

287311
## TODO
288312
- Implement everything from the [JSON API](http://jsonapi.org/) specific.

src/models/json-api.model.ts

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,69 @@
11
import * as _ from 'underscore';
22
import 'reflect-metadata';
3+
import { Headers } from '@angular/http';
4+
import { Observable } from 'rxjs/Observable';
35
import { JsonApiDatastore } from '../services/json-api-datastore.service';
46

57
export class JsonApiModel {
68

79
id: string;
810
[key: string]: any;
911

10-
constructor(data: any) {
11-
this.id = data.id;
12-
_.extend(this, data.attributes);
12+
constructor(private _datastore: JsonApiDatastore, data: any) {
13+
if (data) {
14+
this.id = data.id;
15+
_.extend(this, data.attributes);
16+
}
17+
}
18+
19+
syncRelationships(data: any, included: any) {
20+
this.parseHasMany(data, included);
21+
this.parseBelongsTo(data, included);
1322
}
1423

15-
syncRelationships(data: any, included: any, datastore: JsonApiDatastore) {
16-
this.parseHasMany(data, included, datastore);
17-
this.parseBelongsTo(data, included, datastore);
24+
save(params?: any, headers?: Headers): Observable<JsonApiModel> {
25+
return this._datastore.saveRecord(this, params, headers);
1826
}
1927

20-
private parseHasMany(data: any, included: any, datastore: JsonApiDatastore) {
28+
private parseHasMany(data: any, included: any) {
2129
let hasMany = Reflect.getMetadata('HasMany', this);
2230
if (hasMany) {
2331
for (let metadata of hasMany){
2432
if (data.relationships[metadata.relationship] && data.relationships[metadata.relationship].data) {
2533
let typeName: string = data.relationships[metadata.relationship].data[0].type;
26-
let objectType = Reflect.getMetadata('JsonApiDatastoreConfig', datastore.constructor).models[typeName];
34+
let objectType = Reflect.getMetadata('JsonApiDatastoreConfig', this._datastore.constructor).models[typeName];
2735
this[metadata.propertyName] = this.getHasManyRelationship(objectType, data, included, metadata.relationship, typeName);
2836
}
2937
}
3038
}
3139
}
3240

33-
private parseBelongsTo(data: any, included: any, datastore: JsonApiDatastore) {
41+
private parseBelongsTo(data: any, included: any) {
3442
let belongsTo = Reflect.getMetadata('BelongsTo', this);
3543
if (belongsTo) {
3644
for (let metadata of belongsTo){
3745
if (data.relationships[metadata.relationship] && data.relationships[metadata.relationship].data) {
3846
let typeName: string = data.relationships[metadata.relationship].data.type;
39-
let objectType = Reflect.getMetadata('JsonApiDatastoreConfig', datastore.constructor).models[typeName];
47+
let objectType = Reflect.getMetadata('JsonApiDatastoreConfig', this._datastore.constructor).models[typeName];
4048
this[metadata.propertyName] = this.getBelongsToRelationship(objectType, data, included, metadata.relationship, typeName);
4149
}
4250
}
4351
}
4452
}
4553

46-
private getHasManyRelationship(objectType: { new(data: any): JsonApiModel; }, data: any, included: any, relationship: string, typeName: string): JsonApiModel[] {
54+
private getHasManyRelationship(objectType: { new(ds: JsonApiDatastore, data: any): JsonApiModel; }, data: any, included: any, relationship: string, typeName: string): JsonApiModel[] {
4755
let relationshipList: JsonApiModel[] = [];
4856
data.relationships[relationship].data.forEach((item: any) => {
4957
let relationshipData: any = _.findWhere(included, {id: item.id, type: typeName});
50-
relationshipList.push(new objectType(relationshipData));
58+
relationshipList.push(new objectType(this._datastore, relationshipData));
5159
});
5260
return relationshipList;
5361
}
5462

55-
private getBelongsToRelationship(objectType: { new(data: any): JsonApiModel; }, data: any, included: any, relationship: string, typeName: string): JsonApiModel {
63+
private getBelongsToRelationship(objectType: { new(ds: JsonApiDatastore, data: any): JsonApiModel; }, data: any, included: any, relationship: string, typeName: string): JsonApiModel {
5664
let id = data.relationships[relationship].data.id;
5765
let relationshipData: any = _.findWhere(included, {id: id, type: typeName});
58-
return new objectType(relationshipData);
66+
return new objectType(this._datastore, relationshipData);
5967
}
6068

6169
}

src/services/json-api-datastore.service.ts

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import * as _ from 'underscore';
12
import { Injectable } from '@angular/core';
23
import { Http, Headers, RequestOptions } from '@angular/http';
34
import { Observable } from 'rxjs/Observable';
@@ -12,27 +13,34 @@ export class JsonApiDatastore {
1213

1314
constructor(private http: Http) { }
1415

15-
query(type: { new(data: any): JsonApiModel; }, params?: any, headers?: Headers): Observable<JsonApiModel[]> {
16+
query(type: { new(ds: JsonApiDatastore, data: any): JsonApiModel; }, params?: any, headers?: Headers): Observable<JsonApiModel[]> {
1617
let options = this.getOptions(headers);
1718
let url = this.buildUrl(type, params);
1819
return this.http.get(url, options)
1920
.map((res: any) => this.extractQueryData(res, type))
2021
.catch((res: any) => this.handleError(res));
2122
}
2223

23-
findRecord(type: { new(data: any): JsonApiModel; }, id: string, params?: any, headers?: Headers): Observable<JsonApiModel> {
24+
findRecord(type: { new(ds: JsonApiDatastore, data: any): JsonApiModel; }, id: string, params?: any, headers?: Headers): Observable<JsonApiModel> {
2425
let options = this.getOptions(headers);
2526
let url = this.buildUrl(type, params, id);
2627
return this.http.get(url, options)
2728
.map((res: any) => this.extractRecordData(res, type))
2829
.catch((res: any) => this.handleError(res));
2930
}
3031

31-
createRecord(type: { new(data: any): JsonApiModel; }, data?: any, params?: any, headers?: Headers) {
32+
createRecord(type: { new(ds: JsonApiDatastore, data: any): JsonApiModel; }, data?: any): JsonApiModel {
33+
return new type(this, { attributes: data });
34+
}
35+
36+
saveRecord(data?: any, params?: any, headers?: Headers): Observable<JsonApiModel> {
37+
let type = data.constructor;
3238
let typeName = Reflect.getMetadata('JsonApiModelConfig', type).type;
3339
let options = this.getOptions(headers);
3440
let relationships = this.getRelationships(data);
3541
let url = this.buildUrl(type, params);
42+
data = _.clone(data);
43+
delete data._datastore;
3644
return this.http.post(url, {
3745
data: {
3846
type: typeName,
@@ -41,14 +49,14 @@ export class JsonApiDatastore {
4149
}
4250
}, options)
4351
.map((res: any) => this.extractRecordData(res, type))
44-
.catch((res: any) => this.handleError(res))
52+
.catch((res: any) => this.handleError(res));
4553
}
4654

4755
set headers(headers: Headers){
4856
this._headers = headers;
4957
}
5058

51-
private buildUrl(type: { new(data: any): JsonApiModel; }, params?: any, id?: string) {
59+
private buildUrl(type: { new(ds: JsonApiDatastore, data: any): JsonApiModel; }, params?: any, id?: string) {
5260
let typeName = Reflect.getMetadata('JsonApiModelConfig', type).type;
5361
let baseUrl = Reflect.getMetadata('JsonApiDatastoreConfig', this.constructor).baseUrl;
5462
let idToken = id ? `/${id}` : null;
@@ -75,24 +83,24 @@ export class JsonApiDatastore {
7583
return relationships;
7684
}
7785

78-
private extractQueryData(res: any, type: { new(data: any): JsonApiModel; }): JsonApiModel[] {
86+
private extractQueryData(res: any, type: { new(ds: JsonApiDatastore, data: any): JsonApiModel; }): JsonApiModel[] {
7987
let body = res.json();
8088
let models: JsonApiModel[] = [];
8189
body.data.forEach((data: any) => {
82-
let model: JsonApiModel = new type(data);
90+
let model: JsonApiModel = new type(this, data);
8391
if (body.included) {
84-
model.syncRelationships(data, body.included, this);
92+
model.syncRelationships(data, body.included);
8593
}
8694
models.push(model);
8795
});
8896
return models;
8997
}
9098

91-
private extractRecordData(res: any, type: { new(data: any): JsonApiModel; }): JsonApiModel {
99+
private extractRecordData(res: any, type: { new(ds: JsonApiDatastore, data: any): JsonApiModel; }): JsonApiModel {
92100
let body = res.json();
93-
let model: JsonApiModel = new type(body.data);
101+
let model: JsonApiModel = new type(this, body.data);
94102
if (body.included) {
95-
model.syncRelationships(body.data, body.included, this);
103+
model.syncRelationships(body.data, body.included);
96104
}
97105
return model;
98106
}

0 commit comments

Comments
 (0)