|
## Abstract CRUD class
|
|
## Data Stores
|
|
We've made an abstract data model class, which individual models extend. Hopefully, this allows for easier data service replacement, if need be. Right now, the template implements JSData v2.x ~~v3.x~~ for data needs, which has additional explanation below. Here are the abstract class methods:
|
|
A [redux](http://redux.js.org/)-style store is set up for each model, using [RxJS](http://rudiyardley.com/redux-single-line-of-code-rxjs/). Each store is constructed with some basic configuration, e.g.:
|
|
* `create`: for POSTing one record at a time. (implements jsdata create method); ARGS: (attrs [, options])
|
|
|
|
* `read`: for GETing one record at a time. (implements jsdata find method); ARGS: (id [, options])
|
|
|
|
* `readList`: for GETing a list of the first page of records of a model. (implements jsdata findAll method); ARGS: ([params] [, options])
|
|
|
|
* `readListPaged`: for GETing _all_ records of this model. (implements custom http request); ARGS: ([params])
|
|
|
|
* `update`: for PUTing one record at a time. (implements jsdata update method); ARGS: (id, [attrs] [, options])
|
|
|
|
* `destroy`: for DELETEing one record at a time. (implements jsdata destroy method); ARGS: (id)
|
|
|
|
|
|
|
|
So, if you look at the configuration of the UserService, it extends AbscractModelService. And in a component, we inject it into the constructor as `User: UserService`, and go to town like so: `this.User.read(id).then()`
|
|
|
|
~~Note: previous versions of JSData used the `bypassCache` jsdata option, but in v3 it is now `force`~~
|
|
|
|
|
|
|
|
|
|
|
|
## JSData & Models (Mappers)
|
|
|
|
* the DataStore is set up in one single place, `src/app/shared/services/store.ts`, and only that place
|
|
|
|
* look to `src/app/shared/models/User.model.ts` as a model for model setup
|
|
|
|
* models import that store and interact with it to ~~defineMapper~~ defineResource or in the case of the abstract model, invoke methods on the store
|
|
|
|
* ~~we make the empty class, which extends `Record` so that we get useful class names for records like `User` or `[User, User, User]` in the console~~
|
|
|
|
* _name_: this is how JSData keeps track of mappers internally, so that relations can be hooked up, etc. ~~It would be nice if the class name could just use this name property, but alas the library does not seem to be written that way~~
|
|
|
|
* _endpoint_: the REST endpoint this model will use when we run CRUD operations
|
|
|
|
* ~~loadRelations: in v3, you have to specify relations to load by the localField name, e.g. ` foo.loadrealations(['barObj'])`; loadRelations is a method available on an individual resource. See example below~~
|
|
|
|
|
|
|
|
* _NOTE_: in an earlier version of this template, the CRUD methods took theJSData promise which resulted from the various methods employed and wrapped it in an Observable, in an attempt to follow the use of RxJS elsewhere in ng2. While this might serve a cool purpose on certain apps (merging streams coming from various sources for use in... something cool), that can definitely be implemented on a case by case basis, and we'll leave the promises alone for now.
|
|
|
|
|
|
|
|
|
|
|
|
#### JSData Relations Example, using JSData v2 ~~v3~~, JSDataHttpAdapter v2 ~~v3~~
|
|
|
|
##### Django Model
|
|
|
|
```python
|
|
|
|
class Book (models.Model):
|
|
|
|
title = models.CharField(max_length=50)
|
|
|
|
authors = models.ManyToManyField(Author)
|
|
|
|
publisher = models.ForeignKey(Publisher, null=True, on_delete=models.SET_NULL)
|
|
|
|
```
|
|
|
|
##### JavaScript JSData config
|
|
|
|
```javascript
|
|
```javascript
|
|
DataStore.defineResource({
|
|
// models.ts
|
|
name: 'Book',
|
|
import { Injectable } from '@angular/core';
|
|
endpoint: 'books',
|
|
import { HttpService } from '../services';
|
|
relations: {
|
|
import { Store, Record } from './store';
|
|
hasOne: {
|
|
|
|
Publisher: {
|
|
|
|
localKey: 'publisher',
|
|
@Injectable()
|
|
localField: 'publisherObj',
|
|
export class Data {
|
|
},
|
|
|
|
},
|
|
public User = new Store(
|
|
hasMany: {
|
|
this.http,
|
|
Author: {
|
|
class User extends Record { private _meta = {endpoint: 'users'}; },
|
|
localKeys: 'authors',
|
|
);
|
|
localField: 'authorObjs',
|
|
public Author = new Store(
|
|
}
|
|
this.http,
|
|
},
|
|
class Author extends Record { private _meta = {endpoint: 'authors'}; },
|
|
},
|
|
);
|
|
});
|
|
public Publisher = new Store(
|
|
|
|
this.http,
|
|
|
|
class Publisher extends Record { private _meta = {endpoint: 'publishers'}; },
|
|
this.Book.readList().then(books => {
|
|
);
|
|
|
|
public Book = new Store(
|
|
// loadedBooks === [
|
|
this.http,
|
|
// {
|
|
class Book extends Record { private _meta = {endpoint: 'books'}; },
|
|
// id: 1,
|
|
);
|
|
// title: 'To Kill a Mockingbird',
|
|
|
|
// authors: [1],
|
|
constructor (private http: HttpService) {}
|
|
// authorObjs: undefined,
|
|
}
|
|
// publisher: 1,
|
|
```
|
|
// publisherObj: undefined
|
|
|
|
// }
|
|
|
|
// ]
|
|
|
|
|
|
|
|
let bookPromises = books.map((book: any) => book.DSLoadRelations());
|
|
## Crud Methods
|
|
Promise.all(bookPromises).then(loadedBooks => {
|
|
* `create`: for POSTing one record at a time. ARGS: (body)
|
|
|
|
* `read`: for GETing one record at a time. ARGS: (id, bypassCache)
|
|
|
|
* `readList`: for GETing a list of the first page of records of a model. ARGS: (params)
|
|
|
|
* `readListPaged`: for GETing _all_ records of this model. ARGS: (params)
|
|
|
|
* `update`: for PUTing/PATCHing one record at a time. ARGS: (id, body, patch)
|
|
|
|
* `destroy`: for DELETEing one record at a time. ARGS: (id)
|
|
|
|
|
|
// loadedBooks === [
|
|
example usage:
|
|
// {
|
|
```javascript
|
|
// id: 1,
|
|
// example.component.ts
|
|
// title: 'To Kill a Mockingbird',
|
|
...
|
|
// authors: [1],
|
|
public constructor (
|
|
// authorObjs: [
|
|
private data: Data,
|
|
// {
|
|
) {}
|
|
// id: 1,
|
|
|
|
// first_name: 'Harper',
|
|
public ngOnInit () {
|
|
// last_name: 'Lee',
|
|
this.data.Book.read(42).subscribe(book => {
|
|
// books: [
|
|
console.log(book);
|
|
// {
|
|
|
|
// id: 1,
|
|
|
|
// title 'To Kill a Mockingbird',
|
|
|
|
// authorObjs: [ { id: 1, ... } ],
|
|
|
|
// ...
|
|
|
|
// }
|
|
|
|
// ]
|
|
|
|
// }
|
|
|
|
// ],
|
|
|
|
// publisher: 1,
|
|
|
|
// publisherObj: {
|
|
|
|
// id: 1,
|
|
|
|
// name: 'HarperCollins',
|
|
|
|
// books: [
|
|
|
|
// {
|
|
|
|
// id: 1,
|
|
|
|
// title: 'To Kill a Mockingbird',
|
|
|
|
// publisherObj: { id: 1, ... },
|
|
|
|
// ...
|
|
|
|
// }
|
|
|
|
// ]
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// ]
|
|
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
...
|
|
``` |
|
``` |
|
|
|
\ No newline at end of file |