facing-dev/vue-facing-decorator

@Model does not work with "required: false"

philippmaurer opened this issue · 4 comments

Current behavior
Add a @Model to a field and provide the property option required: false to it.

@Model({type: String, required: false, default:'Hello World'})
fieldName!: string;

If you do not provide a property modelValue in this szenario, or if you provide it with v-bind rather than v-model (:modelValue="someVar"), changes to the field fieldName inside the component will have no effect and not change any state. No errors or warnings are shown in any logs.

In the same szenario, but with the configuration required: true, Vue shows warning logs for a missing property in the browser logs.


Expected behavior

  • Option 1
    It should not be possible to configure a field annotated with @Model as required: false.

  • Option 2
    A property annotated with @Model and required: false should still function like a normal property in the component, if the property is not provided by the parent.

  • optional / debatable
    @Model should also work if the parent component provides the property using v-bind rather than v-model.


Technical explanation

@Model is mapped to a computed property that in it's set method only emits a update:modelValue event. If no property is provided from the parent, the event is not listened to and the parent does not adjust the value of the property. Therefore the property does not change in the current component.


Workaround

Use @Prop instead, create an internal data field that is in sync with the property and handle the update:modelValue emits manually.

@Prop({type: String, required: false, default:'Hello World'})
modelValue!: string;

fieldName!: string;

mounted() {
  this.alignFieldNameWithModelValue();
}

@Watch('modelValue')
alignFieldNameWithModelValue() {
  if (this.fieldName !== this.modelValue) {
    this.fieldName = modelValue;
  }
}

@Watch('fieldName')
emitFieldNameChange() {
  if (this.fieldName !== this.modelValue) {
    this.$emit('update:modelValue', this.fieldName);
  }
}

I just want to mention that i am willing to contribute in this redards, if a MR is wanted.

@philippmaurer pull requests are always welcome!

@ruojianll any thoughts about the request/functionality here?

In my opinion, v-model is a syntactic sugar to combine prop and event. So it should be compatible with props option. If required is false, it is optional. If default is set, it should have a default value.

v-model could downgrade to a normal prop if required false or default is set.

changes to the field fieldName inside the component will have no effect and not change any state. No errors or warnings are shown in any logs.

We shouldn't change props in current component, props and event are both an one-way dataflow, so we should use event to trigger changes in parent component.

v-model="foo" will be compiled in to :modelValue="foo" and @update:modelValue="(e)=>foo.value=e" by vue template compiler.

Because defineModel declares a prop, you can therefore declare the underlying prop's options by passing it to defineModel...

If you have a default value for defineModel prop and you don't provide any value for this prop from the parent component, it can cause a de-synchronization between parent and child components.

https://vuejs.org/guide/components/v-model.html#component-v-model