Stateful context services and the ContextService abstraction for managing shared application state in GraphDB Workbench.
ContextService API is for.ContextService and its utility types.RepositoryContextService: its fields and methods.ContextService: define fields, extend, get the service, subscribe.subscribeAll.getDeserializedContext().A context service is the single owner of one piece of shared state (selected repository, language, security, …) in
packages/api. Consumers in every package read the current value from it and subscribe to changes. See
state ownership.
The ContextService API (context.service.ts):
ContextServiceThe ContextService class is generic and requires a type parameter TFields that defines the fields the service can
handle. Each field corresponds to a property of the service and is managed via the context map.
Key methods:
updateContextProperty(name, value): sets the value of a property and notifies subscribers.validatePropertyChange(name, value): runs the subscribers’ before-change validations; resolves to false if any rejects.getContextPropertyValue(name) (protected): returns the current value.subscribe(name, callback, beforeChangeValidation?, afterChangeCallback?, skipFirst?) (protected): registers a callback; returns an unsubscribe function.subscribeAll(...): subscribes to every field of the service.SnakeToPascalCase converts SNAKE_CASE field names to PascalCase for method naming.
DeriveContextServiceContract requires an update method for each field. For example, a field SELECTED_REPOSITORY
requires an updateSelectedRepository method.
RepositoryContextServiceRepositoryContextService owns the
repository list and the selected repository.
readonly SELECTED_REPOSITORY = 'selectedRepository';
readonly REPOSITORY_LIST = 'repositoryList';
Updating
updateSelectedRepository(repositoryReference?: RepositoryReference): Promise<void> — resolves the reference against
the repository list, runs the before-change validations, then persists the selection and updates the context.updateRepositoryList(repositories: RepositoryList): voidReading
getSelectedRepository(): Repository | undefinedgetRepositoryList(): RepositoryList — an empty list if none is loaded yet.Subscribing
onSelectedRepositoryChanged(callback, beforeChangeValidationPromise?): () => voidonRepositoryListChanged(callback): () => voidContextService1. Define Context Fields and Parameters
type RepositoryContextFields = {
readonly REPOSITORY_LIST: string;
readonly SELECTED_REPOSITORY: string;
};
type RepositoryContextFieldParams = {
readonly REPOSITORY_LIST: RepositoryList;
readonly SELECTED_REPOSITORY: RepositoryReference;
};
2. Extend ContextService
export class RepositoryContextService extends ContextService<RepositoryContextFields>
implements DeriveContextServiceContract<RepositoryContextFields, RepositoryContextFieldParams> {
readonly SELECTED_REPOSITORY = 'selectedRepository';
readonly REPOSITORY_LIST = 'repositoryList';
updateRepositoryList(repositories: RepositoryList): void {
this.updateContextProperty(this.REPOSITORY_LIST, repositories);
}
getRepositoryList(): RepositoryList {
return this.getContextPropertyValue(this.REPOSITORY_LIST) || new RepositoryList([]);
}
onRepositoryListChanged(callbackFunction: ValueChangeCallback<RepositoryList | undefined>): () => void {
return this.subscribe(this.REPOSITORY_LIST, callbackFunction);
}
// updateSelectedRepository, getSelectedRepository, onSelectedRepositoryChanged …
}
3. Get the Service
import { service, RepositoryContextService } from '@ontotext/workbench-api';
const repositoryContextService = service(RepositoryContextService);
4. Read, Update and Subscribe
const selected = repositoryContextService.getSelectedRepository();
await repositoryContextService.updateSelectedRepository({id: 'my-repo', location: ''});
const unsubscribe = repositoryContextService.onSelectedRepositoryChanged((repository) => {
// runs now with the current value, then on every change
});
// When the consumer is destroyed:
unsubscribe();
undefined), then on every change. Pass
skipFirst = true to subscribe to get only later changes.BeforeChangeValidationPromise. validatePropertyChange
resolves to false if any of them does, and an update method that validates (like updateSelectedRepository) then
leaves the value unchanged. This is how a page with unsaved changes can block switching the repository.subscribeAll subscribes to every string-valued field of the service (getContextFields()). Override
getContextFields() if the service has other string properties.When a persisted context value changes in localStorage (for example in another tab), the
Persistence API passes the raw string to the context service that canHandle the property.
Override getDeserializedContext() to map a property to a function that turns that string back into a model;
RepositoryContextService parses the stored RepositoryReference and looks the repository up in the list.
See also: Developers Guide