typescript - 扩展 vuetify VTextField
问题描述
我正在尝试扩展一个 vuetifyVTextField
组件以创建一个可重用的password-field
. 有许多 props 可以控制我们需要改变的组件。Vuejs 认为 prop 突变是一种“反模式”并警告它。
我已经尝试过声明一个计算属性来覆盖有效的道具,但它会在网络控制台中抛出有关冲突的警告。
这是一个简单的例子:
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'
}
}
})
感觉应该不用使用 mixins 来扩展 VTextField 并有选择地删除我们想要用计算属性替换的道具。最后,我们需要该值是响应式的并且在组件的控制之下password-field
——不受父组件的控制。
我在这里走错方向了吗?
更新
借助Yom S () 提供的专家建议,我能够创建VTextField
. 我们采纳了他的建议 #2,一个 SFC 模板化组件。
对于任何偶然发现这个主题的人,这里是 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>
这是一个如何使用该组件的简单示例
<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>
解决方案
如果这个特殊type
的道具是sync
-able,那会很有帮助;但既然不是,您可以通过重新渲染VTextField
, 同时扩展它来解决这个问题。
现在,我不能说这是最好的解决方案,因为它有一些缺点,使其成为有缺陷的包装器。但它确实可以根据您的要求为您提供所需的东西。
常见的缺点:
- 作用域插槽(例如
append
,append-outer
)不会输出预期的元素。
因此,为了这个目的,我们称这个组件为“PasswordField”,我们会像这样使用它:
<PasswordField
label="Enter your password"
:revealed="revealed"
append-outer-icon="mdi-eye"
@click:append-outer="togglePassword" />
和append-outer-icon
图标切换机制可能应该封装在组件本身中。
这里是实现:
密码字段.js
- 优点:
- 稍微简单一些(因为不需要模板)。
- 更快的编译时间,因为它只是一个 JS 文件,不需要经过 Vue 模板编译器和 Vue Loader。(您可以更进一步,使其成为功能组件)。
- 缺点:
- 事件侦听器似乎不起作用。
import { VTextField } from 'vuetify/lib';
export default {
name: 'PasswordField',
extends: VTextField,
props: {
revealed: {
type: Boolean,
default: false
}
},
render(h) {
const { revealed, ...innerProps } = this.$options.propsData;
return h(VTextField, {
// For some reason this isn't effective
listeners: this.$listeners,
props: {
...innerProps,
type: revealed ? 'text' : 'password'
}
})
}
}
请注意extends
基本组件 ( VTextField
) 中的这一点,并且有点“覆盖”原始render
函数,返回自定义的虚拟节点 aka VNode
。
然而,如前所述,这有一些缺点,它无法监听发出的事件。(我很想知道是否有人对此有解决方案)。
因此,作为最后的手段,让我们实际使用模板和计算的道具,字面意思(我们希望该props
部分是唯一要绑定的属性,减去data
)。
密码字段.vue
- 优点:
- 更可靠,功能方面。
- 事件侦听器将按应有的方式工作。
- 当然,SFC 以这种方式效果最好。
- 缺点:
- 有点重复,因为您必须手动(重新)绑定道具并注册事件。
- 较慢的编译时间(无论如何应该几乎不明显)。
<template>
<v-text-field
v-bind="computedProps"
v-on="$listeners">
</v-text-field>
</template>
<script>
import { VTextField } from 'vuetify/lib';
export default {
name: 'PasswordField',
extends: VTextField,
props: {
revealed: {
type: Boolean,
default: false
}
},
computed: {
computedProps() {
return {
...this.$props,
type: this.revealed ? 'text' : 'password'
}
}
}
}
</script>
希望在某种程度上有所帮助!
推荐阅读
- php - PHP MySQL 导出数据到excel
- reactjs - 如何使用 Formik 和 Redux-Saga 处理表单状态
- api - Youtube 数据 API 令牌
- reactjs - 如何样式反应自动完成输入框?
- python - Pandas 根据特定列值对数据框中的行进行分组
- ios - 将私有应用上传到应用商店
- java - Hibernate:如何使用第三个表将属性与条件连接起来
- android - 如何迭代所有数据 SQLite 并完全分配给 Hashmap?
- windows-10 - 如何防止 Service Fabric 自动启动?
- r - 结合两个条形图用ggplot比较数据