For the setup of Mobx-spine-ts a very basic typescript & jest is used. They are linked together using 'ts-jest' For documentation see:
Mobx-spine-ts can be build using yarn build
. This creates a 'dist' directory, with the compiled javascript files, and the typedefinition file.
Under the hood yarn build
simply calls tsx -b
.
Test can be run by using yarn test
.
Under the hood yarn test
calls jest
. For additional help, you can run yarn test --help
.
The BinderApi has a baseUrl variable, which can be overwritten, and is prepended to all request:
const api = new BinderApi();
api.baseUrl = '/api/foo/';
If you now do
api.get('bar/')
it actually queries /api/foo/bar/
Custom error handling can be added by overwriting the onRequestError
method:
const api = new BinderApi();
api.onRequestError = reason => handleReason(reason);
Bypassing the custom error handler can in turn be done by setting skipRequestError
in the request option
Headers can be set in the request options under headers
. E.g. the following sets a custome content-type header:
api.get('/api/asdf/', null, {
headers: { 'Content-Type': 'multipart/form-data' },
});
Default headers can be set on the BinderApi object by adding them to the BinderApi.defaultHeaders
dictionary. E.g.
api = new BinderApi();
api.defaultHeaders['X-Foo'] = 'bar';
You can do get request using:
get(url: string, data?: RequestData, options ?: RequestOptions): Promise<object>
You can do post request using:
post(url: string, data?: RequestData, options ?: RequestOptions): Promise<object>
You can do put request using:
put(url: string, data?: RequestData, options ?: RequestOptions): Promise<object>
You can do patch request using:
patch(url: string, data?: RequestData, options ?: RequestOptions): Promise<object>
You can do delete request using:
delete(url: string, data?: RequestData, options ?: RequestOptions): Promise<object>
The following request options can be set
| ** Option ** | ** Response | | skipFormatter | Boolean, when set to true, the get(), post() etc. return the raw AxiosResponse. Otherwise, parses the response, and only return the data returned from the server | | requestParams | Dictionary containing the query params attached to the request. Note that if you do a get request, with data, the data is taken rather than the requestParams | | skipRequestError | When set to true, does not the set error request error handler. INstead propagate the error |
Classes extending the mobx-spine model need to be patched with @tsPatch as well. This is needed for compatability reasons between the old mobx-spine babel compiler, and the new compiler.
The problem is with this pseudoCode
class Model {
constructor(data) {
// Sets the data of the model
this.parse(data);
}
}
class Animal {
@observable id = 1;
}
const animal = new Animal({id: 2})
console.log(animal.id)
In the babel setup, the Animal class gets compiled to
class Animal {
constructor(data) {
this.id = 1;
super(data);
}
}
This will results in the animal.id
being 2, as is expected. In typescript however, this same code gets compiled to:
class Animal {
constructor(data) {
super(data);
this.id = 1;
}
}
This will result in the animal.id
being 1. This is not consistent. As this workflow is an integral part of how mobx-spine
works, the only option was to patch this behaviour. Therefore a @tsPatch annotation was added, which calls an afterConstructor
after calling the constructor. This means that previous piece of code now needs to be written as:
class Model {
constructor(data) {
}
afterConstructor(data) {
// Sets the data of the model
this.parse(data);
}
}
@tsPatch
class Animal {
@observable id = 1;
}
const animal = new Animal({id: 2})
console.log(animal.id)
The animal class now is compiled as:
class Animal {
constructor(data) {
super(data);
this.id = 1;
// This sets this.id = s
this.afterConstrcutor(data);
}
}
- Models need to be annotated @tsPatch
- Model will generated a warning when you are trying to give it a key that doesn't exist, e.g.
new Animal({thisDoesNotExist: 1})
- Base model has a relations method, returning no relations which can be overridden.
- On the model, in the private method
__scopeBackendResponse
mapping has been renamed torelMapping
to be consistent with thefromBackend
method. - The unused
primaryKey
has been removed.
While refactoring, I encountered some stuff I didn't appreciate. We should later decide which of these items is worth refactoring:
- Model.pickFields is allowed to be undefined, but Model.fileFields and omitFields are not (but they are allowed to be an empty array).
- Model.urlRoot is sometimes a function (in Model itself) and sometimes a property (for instance in tests/Animal). We should probably unify this...
- Currently, the relations of Models are returned by a method. This causes linting errors when accessing them because they are technically not properties (even though some JavaScript magic ensures that they will exist at runtime). It would probably be better if they were real properties instead.
- Our JavaScript mobx spine implementation uses some static properties in Model (like
backendResourceName
) and expects subclasses to override this. However, TypeScript doesn't seem to like 'static inheritance'. The JavaScript code would use this.constructor.backendResourceName, but the TypeScript code needs the uglierthis.constructor['backendResourceName']
. We should find a better solution and perhaps ditch all static stuff. - The
Store
class has both aneach
and aforEach
method, but they do exactly the same thing. I kept both to avoid breaking changes, but I would like to delete one of them.