describo / crate-builder-component

A VueJS UI component to build an RO-Crate
MIT License
6 stars 3 forks source link

Why data binding only works one-way at top level? #31

Closed alvinsw closed 1 year ago

alvinsw commented 1 year ago

I have tried using the crate-builder-component in a new simple Vue app and managed to get it running and displaying the crate data. However I just found out that the crate data is bound only at top level. For example, if you have data.crate = { ... } and pass it to :crate="data.crate", you can't do for example data.crate['@graph'][7].name = 'abc' to update a value from outside of the compenent. And neither data.crate['@graph'][7].name is updated when the value in the text box is changed. I know that we can use the @save:crate event to get the current/modified data. But, is there a reason for not properly bound each nested property to its corresponding ui component? It seems to be counterintuitive to how Vue component generally works.

marcolarosa commented 1 year ago

You’ve asked two different questions whose answer is related.

To the first: why is the modified crate data not being reflected in the crate object outside the component.

The way it operates is by design. The component is intended to be a black box that has a defined interface to send data in and one to get data out. It is designed to completely encapsulate crate data handling internal to itself without needing to worry about side effects coming from outside.

To the second: why aren’t properties directly bound to UI elements.

With regard to the implementation; when the component ingests the crate it pulls it apart into an entities array and a properties array. Each entry in the properties array is an object with entityId, propertyId, property name, value and targetEntityId properties. When the crate is rendered to the UI each of these rows is passed into a component that knows how to handle it. For example, if it's a value and the profile says it should be a date then the date component is given the object to render a date picker.

When a user interacts with a data component (eg a text component or date or select) the new value is emitted and passed back up the tree to be patched into the internal structure. Or if a property is added, a placeholder is emitted and patched in to the properties array.

So the data always flows from the parent component to the relevant child component as properties (vue props which are immutable) and changes inside those components (at the edge) are emitted as a patch to the relevant object which is then handled at the top of the component tree. This is very much how vue (and other spa frameworks) data flow is supposed to work.

The design itself came from an earlier iteration of describo which used a database backend (with an entity table and a linked properties table) to store the crate and which worked exceedingly well with very large crates. Indeed this design also allows me to use the internals of this component in another product I’m working on that is database backed because in that case, the patch events can be sent to the db instead of the internal representation.

What you’ve described sounds a lot like what all of the frameworks stopped doing; namely, allowing changes to data in multiple places. In your description, if I’ve understood it correctly, changes could be happening far away from where the data is being handled and by the magic of JS pass by reference and two way data binding everything would remain in sync. That’s not the done thing nowadays.

Why do you want to pass in a crate and then manipulate it from the outer environment?

alvinsw commented 1 year ago

Thanks for explaining it. There is no use case for manipulating a crate from the outer environment yet. It's just that from the user (of the Vue compoment) perspective, the convention is that a v-model binding to a component is usually two-ways. The one-say binding is normally done via {{ xxx }} and v-once. All the exisiting Vue component's library conforms to that convention, eg: element plus, naive ui, etc.

An example of the more complex component is an editable table such as: https://b-editable-table.muhimasri.com/guide/basic-usage.html A user would expect that an object bound via a v-model will get an update from any changes to a cell and vice versa.

marcolarosa commented 1 year ago

https://vuejs.org/guide/components/props.html#one-way-data-flow