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

I can't create an arbitrary number of nested forms: #248

Closed Qsppl closed 4 months ago

Qsppl commented 5 months ago

This is what I'm trying to create:

"Project" creation form.

In the same form, you can write down as many “Contact Persons” of the target “Project” as you need. We do not know in advance how many “Contact Persons” the project will have.

image

Problem:

I cannot implement the functionality of recording an arbitrary number of Contact Persons in the form.

I can't create an arbitrary number of drafts from one model definition, like a store([ContactStore], { draft: true }).

Also I can't create/delete a separate draft via "store direct methods", like a host.contactDrafts.push(store.draft(ContactStore)).

Also, I can't create a structure from drafts as from models, like a host.projectDraft.contacts.push(store.draft(ContactStore)).

Here is a sandbox with a ready-made example of a form:

image

smalluban commented 5 months ago

You can ;) The below example uses two models, each has its own component for the form. You don't have to submit Person drafts, just when the whole form is submitted create them or pass them to one end-point with the project model. Drafts when disconnected from DOM will be cleared.

My mistake, as those are also drafts, then they must be submitted, to be visible for the main model.

https://stackblitz.com/edit/hybrids-simple-counter-vqdzk3?file=src%2Findex.js

EDIT: Models are external to each other (then they get/set independently) or you can create a nested structure of contacts inside of the main model by not passing id: true to the model: contacts: [{ content: ''}] - But i think in your example the first case is valid.

Qsppl commented 5 months ago

Thanks a lot, it works.

Qsppl commented 5 months ago

When saving a nested model, an error occurs:

TypeError: Provided model instance is not a draft

example: https://stackblitz.com/edit/hybrids-simple-counter-ukwwus?file=src%2Findex.js

Qsppl commented 5 months ago

However, I cannot use store.set(Definition, draft) instead of store.submit(draft) because this approach causes problems when updating the model.

smalluban commented 5 months ago

If the model references another model, the draft of that model still references the second one (not its draft).

The draft mode is nothing more like making a copy of the original and making it memory accessible (if the instance is not there). The reference does not create another deep draft. This is a core issue here.

From the store perspective, Project and Person models are separate. It means, that if you want to reference a Person in a Project, you must "save" it first. I know, that what you would like to do, is to "create" it together - project and all of the persons (I assume that your API allows you to do so).

However, it would be more complicated if you edit the existing Project, as it references Persons, which when set will update accordingly. If this is your requirement, you should create a "special" model for this form - this is not that bad - you can spread fields from both original models, create nested persons, and after saving it, clear original models if needed (just not spread the [store.connect] - so both models will be memory-based copies). Then if you need to edit the model you can get the original model with included persons.

To sum up, the store is flexible and it does not always reflect the API data - it should rather reflect the structure needed in the view. In short, my suggestion is just to make a custom "draft" mode - there is not that much magic behind the feature.

Qsppl commented 4 months ago

We accomplished this task by creating a PersonDraft model, but it looks bad. I'll try to write my own implementation of store.draft()