hybridsjs / hybrids

Extraordinary JavaScript UI framework with unique declarative and functional architecture
https://hybrids.js.org
MIT License
3.03k stars 85 forks source link

Add partial loading of model data #259

Closed Qsppl closed 2 months ago

Qsppl commented 2 months ago

Models often need to be partially loaded:

In such cases, you have to create dozens of duplicate stores for each individual use case.

https://codepen.io/qsppl/pen/JjqLoER?editors=0010

const Project = {
    id: true,
    name: "",
    holding: String,
    investor: String
};

const ProjectWithPublicInformation = {
    ...Project,
    [store.connect]: {
        get: (id) => {
            // Here we request free project information through the API to display a list of all projects in the UI.
            return { id: "1", name: "M11 highway" };
        }
    }
};

const ProjectWithPaidInformation = {
    ...Project,
    [store.connect]: {
        get: (id) => {
            // If the user wanted to see more detailed information about the project,
            // he must make another request to receive paid information.
            return {
                id: "1",
                name: "M11 highway",
                holding: "874387",
                investor: "10334"
            };
        }
    }
};

This creates problems when further working with models:

smalluban commented 2 months ago

Data modeling can be hard, especially in bigger projects. It usually depends on the API as well. You don't share how your data is modeled on the backend.

If we assume that your model has one end-point, but there is a parameter like include=param1,param2, then you can do a little bit different modeling on the client side:

const ExtendModel = {
  id: true,
  extendOne: '',
  extendTwo: '',
  [store.connect]: (id) => api.get(id, { include: [...] }),
};

const Model = {
  basicOne: '',
  basicTwo: '',
  extended: ExtendModel,
  [store.connect]: (id) => {
    const data = api.get(id);
    return {
      ...data,
      extended: data.id,
     };
  },
};

In the root model you create a relation to the extended model, but not provide data for it. As the store relations are lazy, the extended model will be only fetched when called, like

store.ready(model.extended) && model.extended.something

Everything is in sync and you can set basic values and extended values separately.

Qsppl commented 2 months ago

Thank you very much, it looks very simple and convenient.

In my case, the target subsets overlap.

Let's say the project has properties: A, B, C, D, E. There are also three views of this project:

And the API request ends up looking like this:

api.project.get(id, { include: ["A", "B", "C"], payBySubscription: "subscription1" })
const Subset1 = {
    id: true,
    A: '',
    B: '',
    C: '',
    [store.connect]: (id) => api.get(id, { include: [...], payBySubscription: "subscription1" }),
};

const Subset2 = {
    id: true,
    C: '',
    D: '',
    E: '',
    [store.connect]: (id) => api.get(id, { include: [...], payBySubscription: "subscription2" }),
};

const Subset3 = {
    id: true,
    A: '',
    E: '',
    [store.connect]: (id) => api.get(id, { include: [...], payBySubscription: "subscription3" }),
};

const Project = {
    basicOne: '',
    basicTwo: '',
    subset1: Subset1,
    subset2: Subset2,
    subset3: Subset3,
    [store.connect]: (id) => {
        const data = api.get(id);
        return {
            ...data,
            subset1: data.id,
            subset2: data.id,
            subset3: data.id,
        };
    },
};

I don't understand how to update such a model. Probably every time you save the model you need to invalidate the cache of all subsets?

const Project = {
    basicOne: "",
    basicTwo: "",
    subset1: Subset1,
    subset2: Subset2,
    subset3: Subset3,
    [store.connect]: {
        get: (id) => {
            const data = api.get(id)
            return {
                ...data,
                subset1: data.id,
                subset2: data.id,
                subset3: data.id,
            }
        },
        set: (id, values) => {
            store.clear(values.subset1)
            store.clear(values.subset2)
            store.clear(values.subset3)

            const data = api.patch(id, values)

            return {
                ...data,
                subset1: data.id,
                subset2: data.id,
                subset3: data.id,
            }
        },
    },
}
Qsppl commented 2 months ago

Thank you, the solution you suggested works, I did not have any difficulties using it.