Search code examples
typescriptvue.jsvuejs2vue-class-componentsvue-property-decorator

Computed getter/setter for vue-models (therefore, "props") in vue-class-component syntax


In thread Which limitations v-model has in Vue 2.x?, I learned how to link parent and child components v-model. The suggested solution is:

--- ParentTemplate:
<Child v-model="formData"></Child>

-- ChildTemplate:
<input v-model="localValue">

-- ChildScript:
computed: {
    localValue: {
      get() {
        return this.value;
      },
      set(localValue) {
        this.$emit('input', localValue);
      },
    },
  },

Unfortunately, I could not re-write it to vue-class-component syntax. Below code neither works nor should work:

export default class TextEditor extends Vue {

  @Prop({ type: String, required: true }) private readonly value!: string;


  private get localValue(): string {
    return this.value;
  }

  private set localValue(newValue: string) {
    this.$emit("input", newValue);
  }
}

The answer to question how to write computed setters in class-based components in vuejs is not applicable to vue component properties because properties are readonly. So I can't write this.value = newValue.

Problem with straight value usage##

<EditorImplementation 
  :value="value" 
  @input="(value) => { onInput(value) }" 
/>
@Component({
  components {
    EditorImplementation: CK_Editor.component
  }
})
export default class TextEditor extends Vue {

  @Prop({ type: String, required: true }) private readonly value!: string;


  @Emit("input")
  private onInput(value: string): void {
    console.log("checkpoint");
    console.log(this.value);
  }
}

Assume that initially value is empty string.

  1. Input "f"
  2. Log will be "checkpoint" ""
  3. Input "a"
  4. Log will be "checkpoint" "f"
  5. Input "d"
  6. Log will be "checkpoint" "fa"

And so on.


Solution

  • At the moment, it seems like you get an input value from the parent, and then you change the value, and emit that value back to the parent. That seems like an anti-pattern.

    Please try this

    Your EditorImplementation component will be something like

     <input
      ....
      :value="value"
      @input="$emit('input', $event.target.value)"
     />
     
    @Prop({default: ''}) readonly value!: string

    And then

    <EditorImplementation 
      v-model="localValue"
      @input="(value) => { onInput(value) }" 
    />

    and you import it into the Text Editor file like you have done

    @Component({
      components {
        EditorImplementation: CK_Editor.component
      }
    })
    export default class TextEditor extends Vue {
      private localValue = '';
    
      @Emit("input")
      private onInput(value: string): void {
     
      }
    }