Controller
About controllers
Most data hooks return a controller as part of their output. This controller can then be passed to one of the table components or it can be used to directly interact with the data.
We have ListController
and ObjectController
depending on the hook and its input.
Their types are as follows:
export type ObjectController<T> = {
/* The endpoint of the object */
readonly endpoint: string;
/** Amount of layers of references to fill */
readonly depth: Depth;
/** Indicate that the resource should be re-fetched from the remote resource */
invalidate?(): void;
/** Partial update the object */
setObject?: OnChangeObj<T>;
};
export type ListController<T extends Base = Base> = {
/* The endpoint of the object */
readonly endpoint: string;
/** Total amount of elements in the remote resource */
readonly count?: integer;
/** Amount of layers of references to fill */
readonly depth: Depth;
/** Indicate that the resource should be re-fetched from the remote resource */
invalidate?(): void;
/** Partial update a specific object */
setObjectAt?(id: number, obj: PartialRec<T>): void;
/** Add a new object to the list */
addObject?(obj: Omit<PartialRec<T>, "id">): void;
/** Delete a specific object */
deleteObject?({ id }: { id: number }): void;
/** Amount of pages to offset the result by */
readonly page: integer;
setPage(newPage: integer): void;
/** Amount of elements per page */
readonly limit: integer;
setLimit(limit: integer): void;
/** (TODO) Filter settings */
readonly filter: Filter<T>;
setFilter(filter: Filter<T>): void;
filterFunc(value: T, filter: Filter<T>): boolean;
/** Sort value and direction (see below) */
readonly sort: Readonly<Sort>;
setSort({ value, direction }: Sort): void;
/** Search string to filter by inclusion */
readonly search: string;
setSearch(search: string): void;
/** Is the list too long to comfortably fit in browser memory */
readonly partitioned: boolean;
};
Partitioned lists
Longer lists can be split up and retreived in sections. We call these lists partitioned
. In general we recommend using a partitioned lists if the lists is too big to comfortably fit into memory, for example, when it contains more than 100 items. There are some differences:
non-partitioned list When partitioned=false
, we perform the non-mutating list operations client-side. For example, sorting, filtering, searching and paginating is done on the client. This is possible because we have access to the whole list at the client side. This means that the backend should also return the whole list and ignore all limiting parameters.
partitioned list When partitioned=true
, the before mentioned operations cannot be performed on the client, because we simply don't have the required data. For each change of the sort
, limit
, page
, filter
and search
properties, we need to send a new request to the server. Moreover, we need to serialize all these values. In the case of filter
, this means that we need to encode this object and send it to the server. This operation is done in the requestConverter
and needs to be defined in dev-spece. Also on the back-end, the serialized properties should be parsed and applied appropriately. Note that we cannot perform much caching in this case.
Invalidate
Sometimes it is necessary to perform some side-effects outside of the scope of Eidos that invalidate the data stored in the cache. In this case, you can call invalidate
. The client side cache will forget the data that has been stored and a new request is sent to the server.
Sorting
The Sort
type above is defined by:
type Sort = {
/** Represents the column / property to sort by */
value: any;
direction: SortDirection;
};
type SortDirection = "ASC" | "DESC";
Filtering
The Filter
type above is defined by:
type Filter<T extends Base> = Partial<Record<keyof T, string>>;
Filtering behaves differently depending on whether the list is partitioned. If the list is partitioned, all filtering logic happens on the backend. This means that any change to the filter property is passed to the backend (while passing through the requestConverter
). Note that the filterFunc
property is not used in this case. If the list is not partitioned, all filtering is performed on the frond-end. If you want a custom filtering, it is possible to inititalize the filterFunc
property.
useListController
To get a standalone ListController
object, you can use the useListController
hook. It takes an optional initialController
argument.
useArrayController
Sometimes, we have to work with arrays. But these arrays are not used in Eidos. To allow the conversion between an array arr
and a function onChange: (arr: T[]) => void
, we have the useArrayController
hook.
This hook takes an array and an onChange function, and converts this into an Eidos list and ListController pair.