// src/models/dependent/types/index.d.ts
declare interface IDependentModelOptions {
color: string;
}
Create a new file at src/models/dependent/types/index.d.ts
Create the model Options
// src/models/dependent/types/index.d.ts
declare interface IDependentModelOptions {
color: string;
}
Create the model specification
// src/models/dependent/types/index.d.ts
declare interface IDependentModel
extends IDependentModelOptions,
IKosDataModel {
id: string;
}
Create a new file at src/models/dependent/dependent-model-factory.ts
import { modelFactory } from "@coca-cola/kos-ui-core";
export const DEPENDENT_MODEL_TYPE = "dep";
export const DependentModelFactory = modelFactory<
IDependentModel,
IDependentModelOptions
>(DEPENDENT_MODEL_TYPE);
// src/models/dependent/services/dependent-model-services.ts
import { ServiceFactory, resolveServiceUrl } from "@coca-cola/kos-ui-core";
const { URL } = resolveServiceUrl("DEPENDENT_MODEL_SERVICE");
const { getOne } = ServiceFactory.build({
destinationAddress: "",
basePath: `${URL}/dependent`,
});
interface DependentModelResponse {
color: string;
}
/**
* @category Service
* Retrieves the list of Directorys.
*/
export const getDependentData = async () => {
const response = await getOne<DependentModelResponse>({});
return response;
};
Create a new file at src/models/dependent/dependent-model.ts
// src/models/dependent/dependent-model.ts
import {
kosModel,
kosTopicHandler,
KOS_MODEL_ID,
} from "@coca-cola/kos-ui-core";
import type { ApiCallback } from "@coca-cola/kos-ui-core";
import { DependentModelFactory } from "./dependenty-model-factory";
import { getDependentData } from "./services/dependent-model-services";
import log from "loglevel";
import { kosAction } from "@coca-cola/kos-ui-components";
@kosModel<IDependentModel, IDependentModelOptions>(DependentModelFactory.type)
export class DependentModel implements IDependentModel {
id: string;
color: string;
constructor(modelId: string, options: IDependentModel) {
// assign the id from the passed in model id
this.id = modelId;
this.color = options.color;
}
async init(): Promise<void> {
log.info("initialized dependent model");
}
/**
* The load lifecycle method provides an opportunity to hydrate the model with data fetched
* from the backend.
*
*/
async load(): Promise<void> {
log.info("loading dependent model");
try {
const response = await getDependentData();
log.info(`received response ${response}`);
kosAction(() => {
const newColor = response?.data.color;
if (newColor) {
this.color = newColor;
}
});
// alternately could call updateName rather than using the kosAction closure.
} catch (e) {
log.error(e);
throw e;
}
}
@kosTopicHandler({
topic: `/dependent/update`,
websocket: true,
})
handleUpdate(data: ApiCallback) {
log.info("update received");
const body: { color: string } = JSON.parse(data.body);
log.warn(`updating color to ${body.color}`);
this.color = body.color;
}
}
// src/models/dependent/dependent-model-creator.ts
import type { IKosDataModelCreator } from "@coca-cola/kos-ui-core";
import { DependentModel } from "./dependent-model";
export const dependentModelCreator: IKosDataModelCreator<IDependentModel> = ({
modelTypeId,
id,
}) => {
const options = { id: id || modelTypeId, color: "lightblue" };
return new DependentModel(id || modelTypeId, options);
};
Add the new model to the registry for the application at src/App.tsx
.
// src/App.tsx
import { DemoFactory } from "./models/demo/demo-factory";
import { DemoModel } from "./models/demo/demo-model";
import { DependentModel } from "./models/dependent/dependent-model";
import { dependentModelCreator } from "./models/dependent/dependent-model-creator";
export const Registry: IKosRegistry = {
models: {
[DemoFactory.type]: {
class: DemoModel,
},
[DependentModelFactory.type]: {
class: DependentModel,
create: dependentModelCreator,
singleton: true,
},
},
preloadModels: [DependentModelFactory.type],
};
// /src/models/demo/demo-model.ts
import {
kosModel,
kosTopicHandler,
kosDependency,
KOS_MODEL_ID,
} from "@coca-cola/kos-ui-core";
import type { ApiCallback } from "@coca-cola/kos-ui-core";
import { DemoFactory } from "./demo-factory";
import { getDemo } from "./services/demo-services";
import log from "loglevel";
import { kosAction } from "@coca-cola/kos-ui-components";
import { DependentModelFactory } from "../dependent/dependenty-model-factory";
@kosModel<IDemoModel, IDemoModelOptions>(DemoFactory.type)
export class DemoModel implements IDemoModel {
id: string;
name: string;
lastUpdate: number;
@kosDependency({ modelType: DependentModelFactory.type })
depModel!: IDependentModel;
constructor(modelId: string, options: IDemoModelOptions) {
// assign the id from the passed in model id
this.id = modelId;
this.name = options.name;
this.lastUpdate = 0;
}
get backgroundColor() {
return this.depModel?.color || "red";
}
get computedName() {
return `${this.name} - ${this.lastUpdate}`;
}
async init(): Promise<void> {
console.log("initialized model");
}
/**
* The load lifecycle method provides an opportunity to hydrate the model with data fetched
* from the backend.
*
*/
async load(): Promise<void> {
try {
log.info("loading demo model");
const response = await getDemo(this.id);
log.debug(`received response ${response}`);
kosAction(() => {
const newName = response?.data.name;
if (newName) {
this.name = newName;
}
});
} catch (e) {
log.error(e);
throw e;
}
}
@kosTopicHandler({
topic: `/demo/update/${KOS_MODEL_ID}`,
websocket: true,
})
handleUpdate(data: ApiCallback) {
log.info("update received");
const body: { update: number } = JSON.parse(data.body);
this.lastUpdate = body.update;
}
}
Modify the Demo component to allow the created model have it’s id based on passed in props.
This is a bit nonsensical as the model ID would typcially already be known and the instance would likely not be created in the component. However, this is helpful for explaining the concept. |
// src/Demo.tsx
import { kosComponent, useKosModel } from "@coca-cola/kos-ui-components";
import { DemoFactory } from "./models/demo/demo-factory";
interface Props {
indx?: number;
}
export const DemoComponent = ({ indx = 1 }: Props) => {
const { model, ready } = useKosModel<IDemoModel, IDemoModelOptions>({
modelId: `demo${indx}`,
factory: DemoFactory,
options: { name: "demo-name" },
});
const isReady = ready && model;
return isReady ? (
<div style={{ backgroundColor: model.backgroundColor }}>
Hello: {model.computedName}
</div>
) : (
<div>LOADING</div>
);
};
export const Demo = kosComponent(DemoComponent);
// src/App.tsx
function App() {
return (
<ErrorBoundaryWithFallback>
<Suspense fallback={<div>LOADING</div>}>
<KosCoreContextProvider>
<Demo indx={1}></Demo>
<Demo indx={2}></Demo>
</KosCoreContextProvider>
</Suspense>
</ErrorBoundaryWithFallback>
);
}
export default App;