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):
<base-select>
in the childThis 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?
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.