Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

defineModel loses two way binding flow when parent adds event listeneres #10560

Closed
Saeid-Za opened this issue Mar 20, 2024 · 2 comments
Closed

Comments

@Saeid-Za
Copy link
Contributor

Saeid-Za commented Mar 20, 2024

Vue version

3.4.21

Link to minimal reproduction

https://play.vuejs.org/#eNqVUk1vFDEM/Ssml7ZS2Tn0tppWfKioIG1BgDjlMsx4pilZJyTOsqvR/HecDN3dVlCJW/L8bD/bb1SvvV9sEqqlqmMbjGeIyMlfaTJr7wLDavfWyYuQGPrg1nCyqI6wnHyiSVPrKEoyN2zaFcbYDAiXoNUNWuu0ypQ+UcvGEeDa8+4dnZ6NmgCqCm4d3xkagJ20R7jBgPBC06SprmZZIkg+LJm2YZQfQH0sbbl2HdpvjU14qdUjGVrBq+Q7SXtM+qNCq2ou9z38Z9mcV1d7TepccZQ19GZY3EdHstMyn1at1DMWw0efx49aLaFEcqyR9fz6UDAOCc8f8PYO2x9/we/jNmNafQoYMWxExz7GTRiQ5/D1l1vcynsflEmSFfYzwc8YnU1Z40x7k6gT2Ue8ovZ9cYbc62u83jJSfBgqC83MqfC1Em/kVf5r9IPci8VFyZOTyxaf2OupN8E2NMg1WKrJBWbjHe4kruuwN4SrDNWRgyi9Oh0D/kwmYLeEvrERp7PnzWXIJwbe+Xx4LtuCzcvSRoBDO4Fn/4zjkYipWPdgjek3xC4tXg==

Steps to reproduce

There are two inputs in the playground.
<MyInput :modelValue="staticMessage" @update:modelValue="emptyFn"/>
and
<MyInput :modelValue="staticMessage"/>

MyInput is a simple input that shows the current value, and is using defineModel for it's modelValue.

  1. Change first component input in preview tab
  2. Change second component input in preview tab
  3. See that first instance of MyInput does not update and the second does update.

What is expected?

First instance of MyInput should behave exactly like the second one.

What is actually happening?

We're adding a simple event listener, which is passive and like any other event listener should be appended to other listeners, but instead is somehow causing the inner modelValue to be fixated on the initial static value.

For example, when I don't use onMounted hook as an way to listen to mount event, I don't expect the component to never be mounted. this is an event listener, hooking into a fixed behavior, whether I listen to it or not.
This is the same scenario here.

System Info

System:
    OS: Linux 6.6 Ubuntu 22.04.2 LTS 22.04.2 LTS (Jammy Jellyfish)
    CPU: (20) x64 12th Gen Intel(R) Core(TM) i7-12700H
    Memory: 7.24 GB / 15.35 GB
    Container: Yes
    Shell: 5.8.1 - /usr/bin/zsh
  Binaries:
    Node: 20.11.0 - /usr/local/bin/node
    Yarn: 1.22.19 - /usr/bin/yarn
    npm: 10.2.4 - /usr/local/bin/npm
    pnpm: 8.15.1 - /usr/local/bin/pnpm
    bun: 1.0.25 - ~/.bun/bin/bun

Any additional comments?

I'm not sure if this is intentional or not. I've cheeked every related defineModel issue but this is not similar to other issues (open or closed) here.

The usecase is replacing useVModel with defineModel for wrapper components where a default (model) value is provided to a self-controlled child component, meaning the child controls it's model value (synced with parent) without introducing another defaulModelValue prop.

More context: radix-vue

@Saeid-Za Saeid-Za changed the title defineModel loses two way binding when parent adds event listeneres defineModel loses two way binding flow when parent adds event listeneres Mar 20, 2024
@LinusBorg
Copy link
Member

LinusBorg commented Mar 21, 2024

When you provide the modelValue prop and an update:modelValue listener (which is the same as providing the v-model directive), it's expected that the parent handles the state, meaning defineModel does not handle local state updates.

Since your update listener breaks this contract, the local state doesn't change, as it expects to receive an update via the modelValue prop

This is the default behaviour as this would also happen when you provide the prop and event options in the child instead of using defineModel.

Only when the prop value or event listener are not provided by the parent, defineModel falls back on handling its state internally. It's true though that this is info is missing in the current docs.

See: #10351

@Saeid-Za
Copy link
Contributor Author

Saeid-Za commented Mar 21, 2024

Thank you for your response.
I understand your point and the design decision made here but I as a developer want more granular control on whether I want my prop to be internally or externally controlled via the defineModel macro,

providing a default static value and an event listener should not be an indicator of this decision.

Somthing that is done in useVModel:

  /**
   * When passive is set to `true`, it will use `watch` to sync with props and ref.
   * Instead of relying on the `v-model` or `.sync` to work.
   *
   * @default false
   */
  passive?: Passive

Where can I propose such change in vue/rfcs ? defineModel is stable and the proposal is closed now.


  1. On another unrelated note, this should be documented too.

Assigning model.value = model.value won't trigger local changes - but it always emits the update:modelValue event.

changing the value of model would trigger an emit, whether the model has changed or not.

  1. And also from this PR, useModel is not documented too. it seems that what i desire is a macro version of useModel.

If it's OK I can help document these missing parts.

@github-actions github-actions bot locked and limited conversation to collaborators Apr 5, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants