Exploring the idea of extending a vuetify VTextField
component to create a reusable password-field
, I encountered warnings against prop mutation in Vuejs, deeming it an "anti-pattern".
One approach I tried involved using a computed property to override the prop, which did work but resulted in conflict warnings in the web console.
Below is a basic example:
import Vue from 'vue'
import { VTextField } from 'vuetify/lib'
export default Vue.extend({
name: 'password-field',
mixins: [VTextField],
data: () => ({
reveal: false
}),
computed: {
function type () {
return this.reveal ? 'text' : 'password'
}
}
})
It seems like there should be a way to utilize mixins to extend VTextField and selectively replace props with computed properties. The ultimate goal is to have a reactive value under the control of the password-field
component rather than the parent.
Could my current approach be incorrect?
UPDATED
After receiving valuable input from Yom S, I successfully created a custom extension of VTextField
. We opted for suggestion #2, an SFC templated component.
For others who may come across this topic, here is an implementation compatible with Typescript:
<!-- put this in components/password-field.vue -->
<template>
<v-text-field
v-bind="computedProps"
v-on:click:append="reveal = !reveal"
v-on="listeners$"
></v-text-field>
</template>
<script lang="ts">
import Vue from 'vue'
import { VTextField } from 'vuetify/lib'
export default {
name: 'PasswordField',
extends: VTextField,
props: {
label: {
type: String,
default: 'Password'
},
rules: {
type: Array,
default: () => [(v: string) => {
return /((?=.*\d)(?=.*[a-z])(?=.*[!@#$%^&*()?.]).{8,})/i.test(v) ||
'At least 8 char; upper and lowercase, a number and a special char'
}]
}
},
data: () => ({
reveal: false
}),
computed: {
computedProps: function () {
return {
...this.$props,
type: this.reveal ? 'text' : 'password',
appendIcon: this.reveal ? 'mdi-eye' : 'mdi-eye-off'
}
}
}
} as Vue.ComponentOptions<Vue>
</script>
Here's a simple example demonstrating how to use this component:
<template>
<v-form v-model="formValid">
<password-field v-model="newPassword/>
<v-btn :disabled="!formValid">Change</v-btn>
</v-form>
</template>
<script lang="ts">
import Vue from 'vue'
import PasswordField from '@/components/password-field.vue'
export default Vue.extend({
name: 'ChangePasswordForm',
data: () => ({
formValid: false,
newPassword: ''
})
})
</script>