# ServiceModel
A ServiceModel
extends from BaseModel
and adds the ModelManager
with a caching
store to keep track of aggregation of running requests and optionally caching the result of the services.
import {ServiceModel, Field} from 'vue-service-model'
class Album extends ServiceModel {
static urls = 'https://jsonplaceholder.typicode.com/albums/'
// Duration to cache requested data in seconds. 0: no cache. null: Cache forever. Default is 30 seconds
static cacheDuration = 5
static fieldsDef = {
id: new Field({primaryKey: true}),
title: new Field()
}
}
# Urls
Urls are currently divided into 2 different types. LIST
and DETAIL
(same like in Django REST framework (opens new window)).
LIST
: (e.g./albums/
) used forobjects.list()
DETAIL
: (e.g./albums/1/
) used forobjects.detail(1)
The simplest way to define the urls is to set the static property urls
in your ServiceModel
.
static urls = 'https://jsonplaceholder.typicode.com/albums/'
When performing a detail request your key will be automatically appended to the end of the url.
You can also define the LIST
and DETAIL
url separately:
static urls = {
LIST: 'https://jsonplaceholder.typicode.com/albums/',
// {pk} will be replaced with your value you provide to objects.detail()
DETAIL: 'https://jsonplaceholder.typicode.com/albums/{pk}/'
}
There are currently 3 ways to define your url with the following priority
- Overwrite
getListUrl
orgetDetailUrl
method and a return aPromise
which will resolve the url as a string - Set the
LIST
orDETAIL
url in your modelstatic urls = { LIST: <...>, DETAIL: <...> }
- Set the base url in your model as string
static urls = '<...>'
orstatic urls = { BASE: <...> }
If you got a nested RESTful service structure (e.g. /albums/1/photos/
) have a look at parents.
# Service methods
There are several methods to make it even more easy to do service requests in relation to your model instance. It is required to define a primary key field in your model definition.
# Reloading model data
To reload the data of your current model instance you can call myModel.reload()
.
This method will send a detail request (equal to myModel.objects.detail()
) and does not use the cache.
It is required that your primary key field is filled otherwise the method will return false
.
WARNING
Any changes made to your model data will be overwritten
const album = await Album.objects.detail(1) // Request: GET /albums/1/
[...]
await album.reload() // Request: GET /albums/1/
# Updating your model data
In case you want to save your local model data you can either use myModel.update()
. It is required that your primary key field is filled otherwise the method will return false
.
myModel.update()
will internally call the ModelManager
method objects.update()
with your model primary key, parents and data.
const album = await Album.objects.detail(1) // Request: GET /albums/1/
album.val.title = 'New title'
await album.update() // Request: PUT /albums/1/
You can give a list of field names as updateFields
to the update
method to do a partial update request (PATCH).
await album.update({ updateFields: ['title'] }) // Request: PATCH /albums/1/
The model data will be mapped by mapPartialUpdateFields
which will call mapFieldValue
on every field you provide in updateFields
.
# Creating a new model record
To create a new record you can call myModel.create()
.
myModel.create()
will internally call the ModelManager
method objects.create()
with your model parents and data.
const album = new Album({title: 'New album'})
await album.create() // Request: POST /albums/
# Create or update a model instance
When calling myModel.save()
the method will check if the primary key of your model instance is filled. If so myModel.update()
if not myModel.create()
will be called.
The return value is a boolean flag if the myModel.create()
has been called or not.
const album = new Album({title: 'New album'})
console.log(album.pk) // Output: null
await album.save() // Request: POST /albums/
console.log(album.pk) // Output: 5
await album.save() // Request: PUT /albums/5/
# Deleting a model record
To delete an existing record you can call myModel.delete()
.
It is required that your primary key field is filled otherwise the method will return false
.
myModel.delete()
will internally call the ModelManager
method objects.delete()
with your model primary key and parents.
const album = await Album.objects.detail(1) // Request: GET /albums/1/
[...]
await album.delete() // Request: DELETE /albums/1/
# Aggregation
When you start to request data from a service, for example Album.objects.detail('1')
, then the Promise
of the request will
be saved as long as the request has not been completed. So when requesting Album.objects.detail('1')
again (e.g from another component)
this request will be attached to the first request which has not been completed yet and the request of the service will only made once.
In case you want to avoid the request aggregation for a specific request see noRequestAggregation
in ModelManager RetrieveInterfaceParams.
# Cache
With the static property cacheDuration
it is possible to set the duration (in seconds) of how long the result of a response
should be cached. The default value is 30 seconds. Currently the expired data will only be removed by requesting the same data again.
- null: cache will not be removed
- 0: no caching
You can manually clear the complete cache including aggregation by calling model.store.clear()
.
In case you want to set cache options for a specific request see ModelManager RetrieveInterfaceParams.
By default the response of a list request will not be cached.
# Parents
When using a nested RESTful service more information is necessary to uniquely identify a resource. You need to define parentNames
in your ServiceModel
.
class Photo extends ServiceModel {
[...]
// Define name of parents
static parentNames = ['album']
// Add placeholder for parent in your url
static urls = 'https://jsonplaceholder.typicode.com/albums/{album}/photos/'
}
// Retrieve all photos from album 1: /albums/1/photos/
const photos = await Photo.objects.list({parents: {album: 1}})
// Retrieve photo 2 from album 1: /albums/1/photos/2/
const photo = await Photo.objects.detail(2, {parents: {album: 1}})
// Access parents of photo
photo.parents
It is necessary to set exact parents otherwise a warning will be printed to the console. You can also add some custom
validation of the parents by extending the checkServiceParents
method of your ServiceModel
. This will be called on default ModelManager
interfaces and when retrieving the service url from getListUrl
or getDetailUrl
.
You can provide parents to your model instance via the constructor or manually set with photo.parents = {album: 1}
.