1

I'm using Vue v2 with Typescript and I'm trying to extend a parent class:

My parent class is called BaseSelect and looks like this:

<template>
  <select :value="value" @change="$emit('change', $event.target.value)">
    <option value="">default option</option>
    <slot />
  </select>
</template>

<script lang="ts">
import { Component, Model, Vue } from 'vue-property-decorator';

@Component({})
export default class BaseSelect extends Vue {
  @Model('change', { type: String, required: false, default: '' })
  private readonly value!: string

  private valid = true;

  validate(): boolean {
    this.valid = !!this.value;
    return this.valid;
  }
}
</script>

My child class BaseSelectGender looks like this:

<template>
  <base-select :value="value" @change="$emit('change', $event)">
    <option value="male">I'm male</option>
    <option value="female">I'm female</option>
  </base-select>
</template>

<script lang="ts">
import { Component } from 'vue-property-decorator';
import { BaseSelect } from '@/components/base';

@Component({
  components: { BaseSelect }
})
export default class BaseSelectGender extends BaseSelect {}
</script>

When I use <base-select-gender> in my code there are two instances of the BaseSelect component (and therefore two different instances of the valid variable):

  1. The first instance which is created because of the inheritance
  2. The second instance which is created because of the usage of <base-select> in the child

This leads to some problems when the valid variable changes because the wrong instance of the variable is reflected in the DOM.

So my question is now: How can I extend a base class and also use it or at least extend the html code in the template part of my child component?

2
  • Did you figure it out ? :) Commented Mar 23, 2021 at 12:22
  • @Boern yes, but it's only a workaround. I'll add it as an answer below. Commented Mar 24, 2021 at 9:08

1 Answer 1

2

Workaround

So I found a workaround where I'm using the ref attribute to have access to the attributes and methods of the "inner" component (the one in the template section).

This allows me to manually synchronize all the attributes that I need (make sure that the required attributes do not have a private modifier).

My BaseSelectGender component now looks like this:

<template>
  <base-select 
    :value="value" 
    @change="$emit('change', $event)"
    v-bind="{ ...$attrs, ...$props }"
    ref="inner"
  >
    <option value="male">I'm male</option>
    <option value="female">I'm female</option>
  </base-select>
</template>

<script lang="ts">
import { Component } from 'vue-property-decorator';
import { BaseSelect } from '@/components/base';

@Component({
  components: { BaseSelect }
})
export default class BaseSelectGender extends BaseSelect {
  get inner() {
    return this.$refs.inner as BaseSelect;
  }

  setValid(valid: boolean) {
    this.valid = valid;
    this.inner.setValid(valid);
  }

  validate(): boolean {
    this.valid = this.inner.validate();
    return this.valid;
  }
}
</script>

Pro-tip: Use v-bind="{ ...$attrs, ...$props }" to pass through all the attributes and props from the outer component to the inner component.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.