Skip to content

Commit edfc2af

Browse files
committed
Add BelongsTo relationship. Fix some relationship parsing issues. Parse included relationships when creating records
1 parent 80d6518 commit edfc2af

File tree

6 files changed

+115
-38
lines changed

6 files changed

+115
-38
lines changed

README.MD

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ import { JsonApiDatastoreConfig, JsonApiDatastore } from 'angular2-jsonapi';
7474
baseUrl: 'http://localhost:8000/v1/',
7575
models: {
7676
posts: Post,
77-
comments: Comment
77+
comments: Comment,
78+
users: User
7879
}
7980
})
8081
export class Datastore extends JsonApiDatastore { }
@@ -83,7 +84,7 @@ export class Datastore extends JsonApiDatastore { }
8384
Then set up your models:
8485
- Extend the `JsonApiModel` class
8586
- Decorate it with `@JsonApiModelConfig`, passing the `type`
86-
- Decorate the relationships attributes with `@HasMany`
87+
- Decorate the relationships attributes with `@HasMany` and `@BelongsTo`
8788

8889
```typescript
8990
import { JsonApiModelConfig, JsonApiModel, HasMany } from 'angular2-jsonapi';
@@ -106,8 +107,21 @@ export class Post extends JsonApiModel {
106107
export class Comment extends JsonApiModel {
107108

108109
title: string;
109-
110+
111+
@BelongsTo()
110112
post: Post;
113+
114+
@BelongsTo()
115+
user: User;
116+
}
117+
118+
@JsonApiModelConfig({
119+
type: 'users'
120+
})
121+
export class User extends JsonApiModel {
122+
123+
name: string;
124+
// ...
111125
}
112126
```
113127

@@ -191,17 +205,34 @@ this.datastore.createRecord(Post, {
191205
);
192206
```
193207

194-
If the object you want to create has a one-to-many relationship, you can do this:
208+
If the object you want to create has a **one-to-many** relationship, you can do this:
209+
210+
```typescript
211+
this.datastore.createRecord(Comment, {
212+
title: 'My comment',
213+
post: post
214+
}).subscribe(
215+
(comment: Comment) => console.log(comment)
216+
);
217+
```
218+
219+
where `post` is an object of type `Post`, previously retrieved from the API.
220+
221+
If you want to include a relationship when creating a record to have it parsed in the response, you can pass the `params` object:
195222

196223
```typescript
197224
this.datastore.createRecord(Comment, {
198225
title: 'My comment',
199-
post: Post
226+
post: post
227+
}, {
228+
include: 'user'
200229
}).subscribe(
201230
(comment: Comment) => console.log(comment)
202231
);
203232
```
204233

234+
235+
205236
### Custom Headers
206237

207238
By default, the library adds these headers, according to the [JSON API MIME Types](http://jsonapi.org/#mime-types):
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import 'reflect-metadata';
2+
3+
export function BelongsTo(config: any = {}) {
4+
return function(target: any, propertyName: string | symbol) {
5+
let annotations = Reflect.getMetadata('BelongsTo', target) || [];
6+
annotations.push({
7+
propertyName: propertyName,
8+
relationship: config.key || propertyName
9+
});
10+
Reflect.defineMetadata('BelongsTo', annotations, target);
11+
};
12+
}

src/decorators/has-many.decorator.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import 'reflect-metadata';
22

33
export function HasMany(config: any = {}) {
4-
return function(target: any, attributeName: string | symbol) {
4+
return function(target: any, propertyName: string | symbol) {
55
let annotations = Reflect.getMetadata('HasMany', target) || [];
66
annotations.push({
7-
attribute: attributeName,
8-
key: config.key || attributeName
7+
propertyName: propertyName,
8+
relationship: config.key || propertyName
99
});
1010
Reflect.defineMetadata('HasMany', annotations, target);
1111
};

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { PROVIDERS } from './services';
22

33
export * from './decorators/has-many.decorator';
4+
export * from './decorators/belongs-to.decorator';
45
export * from './decorators/json-api-model-config.decorator';
56
export * from './decorators/json-api-datastore-config.decorator';
67

src/models/json-api.model.ts

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,49 @@ export class JsonApiModel {
1313
}
1414

1515
syncRelationships(data: any, included: any, datastore: JsonApiDatastore) {
16+
this.parseHasMany(data, included, datastore);
17+
this.parseBelongsTo(data, included, datastore);
18+
}
19+
20+
private parseHasMany(data: any, included: any, datastore: JsonApiDatastore) {
1621
let hasMany = Reflect.getMetadata('HasMany', this);
17-
for (let metadata of hasMany){
18-
if (data.relationships[metadata.key]) {
19-
let type = Reflect.getMetadata('JsonApiDatastoreConfig', datastore.constructor).models[metadata.key];
20-
this[metadata.attribute] = this.getRelationship(type, data, included, metadata.key);
22+
if (hasMany) {
23+
for (let metadata of hasMany){
24+
if (data.relationships[metadata.relationship] && data.relationships[metadata.relationship].data) {
25+
let typeName: string = data.relationships[metadata.relationship].data[0].type;
26+
let objectType = Reflect.getMetadata('JsonApiDatastoreConfig', datastore.constructor).models[typeName];
27+
this[metadata.propertyName] = this.getHasManyRelationship(objectType, data, included, metadata.relationship, typeName);
28+
}
29+
}
30+
}
31+
}
32+
33+
private parseBelongsTo(data: any, included: any, datastore: JsonApiDatastore) {
34+
let belongsTo = Reflect.getMetadata('BelongsTo', this);
35+
if (belongsTo) {
36+
for (let metadata of belongsTo){
37+
if (data.relationships[metadata.relationship] && data.relationships[metadata.relationship].data) {
38+
let typeName: string = data.relationships[metadata.relationship].data.type;
39+
let objectType = Reflect.getMetadata('JsonApiDatastoreConfig', datastore.constructor).models[typeName];
40+
this[metadata.propertyName] = this.getBelongsToRelationship(objectType, data, included, metadata.relationship, typeName);
41+
}
2142
}
2243
}
2344
}
2445

25-
private getRelationship(type: { new(data: any): JsonApiModel; }, data: any, included: any, relationshipName: string): JsonApiModel[] {
46+
private getHasManyRelationship(objectType: { new(data: any): JsonApiModel; }, data: any, included: any, relationship: string, typeName: string): JsonApiModel[] {
2647
let relationshipList: JsonApiModel[] = [];
27-
data.relationships[relationshipName].data.forEach((item: any) => {
28-
let relationship: any = _.findWhere(included, {id: item.id});
29-
relationshipList.push(new type(relationship));
48+
data.relationships[relationship].data.forEach((item: any) => {
49+
let relationshipData: any = _.findWhere(included, {id: item.id, type: typeName});
50+
relationshipList.push(new objectType(relationshipData));
3051
});
3152
return relationshipList;
3253
}
3354

55+
private getBelongsToRelationship(objectType: { new(data: any): JsonApiModel; }, data: any, included: any, relationship: string, typeName: string): JsonApiModel {
56+
let id = data.relationships[relationship].data.id;
57+
let relationshipData: any = _.findWhere(included, {id: id, type: typeName});
58+
return new objectType(relationshipData);
59+
}
60+
3461
}

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

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -32,27 +32,13 @@ export class JsonApiDatastore {
3232
.catch((res: any) => this.handleError(res));
3333
}
3434

35-
createRecord(type: { new(data: any): JsonApiModel; }, data?: any, headers?: Headers) {
35+
36+
createRecord(type: { new(data: any): JsonApiModel; }, data?: any, params?: any, headers?: Headers) {
3637
let typeName = Reflect.getMetadata('JsonApiModelConfig', type).type;
37-
let baseUrl = Reflect.getMetadata('JsonApiDatastoreConfig', this.constructor).baseUrl;
3838
let options = this.getOptions(headers);
39-
let relationships: any;
40-
for (let key in data) {
41-
if (data.hasOwnProperty(key)) {
42-
if (data[key] instanceof JsonApiModel) {
43-
relationships = relationships || {};
44-
let relationshipType = Reflect.getMetadata('JsonApiModelConfig', data[key].constructor).type;
45-
relationships[key] = {
46-
data: {
47-
type: relationshipType,
48-
id: data[key].id
49-
}
50-
}
51-
delete data[key];
52-
}
53-
}
54-
}
55-
return this.http.post(baseUrl + typeName, {
39+
let relationships = this.getRelationships(data);
40+
let url = this.buildUrl(type, params);
41+
return this.http.post(url, {
5642
data: {
5743
type: typeName,
5844
attributes: data,
@@ -67,14 +53,34 @@ export class JsonApiDatastore {
6753
this._headers = headers;
6854
}
6955

70-
private buildUrl(type: { new(data: any): JsonApiModel; }, params: any = {}, id?: number){
56+
private buildUrl(type: { new(data: any): JsonApiModel; }, params?: any, id?: number){
7157
let typeName = Reflect.getMetadata('JsonApiModelConfig', type).type;
72-
if (params.include && typeof params.include === 'function') {
58+
if (params && params.include && typeof params.include === 'function') {
7359
params.include = Reflect.getMetadata('JsonApiModelConfig', params.include).type;
7460
}
7561
let baseUrl = Reflect.getMetadata('JsonApiDatastoreConfig', this.constructor).baseUrl;
7662
let idToken = id ? `/${id}` : null;
77-
return [baseUrl, typeName, idToken, '?', this.toQueryString(params)].join('');
63+
return [baseUrl, typeName, idToken, (params ? '?' : ''), this.toQueryString(params)].join('');
64+
}
65+
66+
private getRelationships(data: any): any{
67+
let relationships: any;
68+
for (let key in data) {
69+
if (data.hasOwnProperty(key)) {
70+
if (data[key] instanceof JsonApiModel) {
71+
relationships = relationships || {};
72+
let relationshipType = Reflect.getMetadata('JsonApiModelConfig', data[key].constructor).type;
73+
relationships[key] = {
74+
data: {
75+
type: relationshipType,
76+
id: data[key].id
77+
}
78+
};
79+
delete data[key];
80+
}
81+
}
82+
}
83+
return relationships;
7884
}
7985

8086
private extractQueryData(res: any, type: { new(data: any): JsonApiModel; }): JsonApiModel[] {

0 commit comments

Comments
 (0)