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

Suggestion: Develop a solution that allows you to use a single component to create, edit and present a model #225

Closed Qsppl closed 9 months ago

Qsppl commented 9 months ago

It just looks cool.

image

image

image

image

Now, for such a result, you have to create two visually similar components component and component-form, replacing them if it is necessary to edit/create the model. These two components need to be maintained in a consistent state, logic, html and css markup must be duplicated. This makes development so difficult that it becomes easier to completely separate the view and the creation/editing form and place the form in a modal window. And since it’s simpler, that’s exactly how we have to work.

Qsppl commented 9 months ago

I can describe the problem in more detail and write an example. If this approach to development does not correspond to the Hybrids development style, then you can reject the proposal.

smalluban commented 9 months ago

I think you should be able to do something similar to this:

{
  model: store(Model),
  draft: store(Model, { draft: true, id: 'model' }),
  editMode: {
    get: (host, value) => !host.model || value,
    set: (host, value) => value,
  },
  render: ({ model, draft, editMode }) => html`
    ${editMode && store.ready(draft)
      ? html`<input ... value="${draft.content}" />...`
      : html`<div>${store.ready(model) && model.content}</div>`
    }
   ${store.ready(model) && html`<button onclick="${toggleEditMode}">edit</button>`}
  `,
}
Qsppl commented 9 months ago

Previously I tried a similar solution:

{
  modelId: "",
  model: store(Model, { id: 'modelId' }),
  draft: store(Model, { draft: true, id: 'modelId' }),
  editMode: {
    get: (host, value) => !host.model || value,
    set: (host, value) => value,
  },
  render: ({ model, draft, editMode }) => html`
    ${editMode && store.ready(draft)
      ? html`<input ... value="${draft.content}" />...`
      : html`<div>${store.ready(model) && model.content}</div>`
    }
   ${store.ready(model) && html`<button onclick="${toggleEditMode}">edit</button>`}
  `,
}

This didn't work: https://github.com/hybridsjs/hybrids/issues/223

Probably the mistake was that I used the modelId property as the id.

I'll test your solution in the near future.

smalluban commented 9 months ago

I made a working example on StackBlitz, here you have it - https://stackblitz.com/edit/hybrids-store-ui-form-example

Indeed, it didn't work first - I think I found another case - where you pass the property name as id - it is taken as it is, not check if it is a model instance - so id is a stringified whole object. You must set id as a function (for now):

id: ({ model }) => model?.id

However, with the additional modelId it should work as expected. I am not sure why it didn't work for you.

EDIT: This was wrong - I tricked myself - as I wrote in another issue, the id option for the draft should be disabled, and I will do it in the next major release (quite soon). In the version with the id you can't clear out the draft after submitting, as it has no setter (uses the id option, which is created for just this).

I refactored the example, and it works now as expected. The most important part is to use draft without id, and when toggle edit mode pass the model into the draft:

function toggleEdit(host) {
  host.editMode = true;
  host.draft = host.model;
}
Qsppl commented 9 months ago

thanks, this works great.