英文:
Vuetify3- Vue3 V-Bind not working as expected
问题
抱歉,以下是翻译好的部分:
抱歉,在此处编写代码,未使用代码段,但我无法使其正常工作。
我正在为一个项目开发一些通用组件。考虑到这一点,我根据这篇文章使用了一种属性提取策略。
因此,我创建了一个类型
export declare type ExtractComponentProperties<TComponent> = TComponent extends new () => { $props: infer P }
? P
: never;
一些接口用于暴露属性和发射事件
import { Nullable } from '@/types/nullable';
import type { ExtractComponentProperties } from '@/types/component/properties/extract';
import type { ExtractComponentEmits } from '@/types/component/emits/extract';
export interface IComponentExtendedProperties<T> {
properties?: ExtractComponentProperties<T>;
}
export interface IComponentProperties {
[key: string]: unknown;
}
export interface IModelValueComponentProperties<T> extends IComponentProperties {
modelValue?: T | Nullable<T>;
}
export interface IComponentExtendedEmits<T> {
emits?: ExtractComponentEmits<T>;
}
还创建了一些接口,负责暴露提取的值
import { IComponentExtendedProperties } from '@/types/component/properties';
import { IComponentExtendedEmits } from '@/types/component/emits';
export interface IPropertiesBinder<T> extends IComponentExtendedProperties<T> {}
export interface IEmitsBinder<T> extends IComponentExtendedEmits<T> {}
export interface IPropertiesEmitsBinder<T> extends IPropertiesBinder<T>, IEmitsBinder<T> {}
然后,我为每个实现ExtractComponentProperties
的组件创建了一个特定的接口
import { VMenu } from 'vuetify/lib/components/VMenu/index';
import { VBtn } from 'vuetify/lib/components/VBtn/index';
import { VList } from 'vuetify/lib/components/VList/index';
import { IPropertiesEmitsBinder,IPropertiesBinder } from '@/types/component/binder';
export interface IButton extends IPropertiesEmitsBinder<typeof VBtn> {
text?: string;
}
export interface IMenu extends IComponentExtendedProperties<typeof VMenu> {}
export interface IList extends IPropertiesBinder<typeof VList> {
items: IListItem[];
}
export interface IListItem extends IPropertiesEmitsBinder<typeof VListItem> {
id: string;
title?: string;
}
然后,我创建了ButtonDropdown.vue单文件组件
<template>
<VMenu v-bind="useMenuBinder(componentProperties.menu)">
<template v-slot:activator="{ props }">
<slot name="activator" :props="props">
<VBtn v-bind="useButtonBinder(componentProperties.button, props)">
{{ componentProperties.button?.text }}
<VIcon v-if="componentProperties.button?.properties?.icon" class="ml-2">
{{ componentProperties.button.properties.icon }}
</VIcon>
</VBtn>
</slot>
</template>
<VList v-bind="useListBinder(componentProperties.list)">
<slot>
<VListItem
v-for="(item, index) in availableItems"
:key="index"
:value="item"
v-bind="useListItemBinder(item)"
>
<VListItemTitle v-if="item.title">{{ item.title }}</VListItemTitle>
</VListItem>
</slot>
</VList>
</VMenu>
</template>
<script setup lang="ts">
import { IMenu } from '@/types/component/menu';
import { IList } from '@/types/component/list';
import { IListItem } from '@/types/component/list/item';
import { IButton } from '@/types/component/button';
import { v4 as uuidv4 } from 'uuid';
export interface ButtonDropdownComponentProperties {
menu?: IMenu;
button?: IButton;
list?: IList;
items?: IListItem[];
}
const componentProperties = withDefaults(defineProps<ButtonDropdownComponentProperties>(), {
button: () => ({
properties: {
icon: 'mdi-chevron-down',
color: 'transparent',
size: 'small',
elevation: 0,
},
text: { key: 'buttons.dropdown' },
}),
menu: () => ({
properties: {
maxWidth: '300px',
closeOnContentClick: true,
},
}),
list: () => ({
items: [{ id: generateId(), title: 'Example Text' }],
properties: {
minWidth: '200vw',
maxWidth: 'auto',
},
}),
});
const availableItems = computed(() => componentProperties.list?.items ?? []);
function generateId(): string {
return uuidv4();
}
</script>
现在当我尝试调用这个组件时
<template>
<ButtonDropdown
:button="{ text: 'Locale', properties: { icon: 'mdi-translate', elevation: '0' } }"
>
<VListItem v-for="(locale, index) in availableLocales">
{{ locale.text }}
</VListItem>
</ButtonDropdown>
</template>
<script setup lang="ts">
export interface ILocale {
locale: string;
text: string;
};
const localeStore = useLocaleStore();
const availableLocales = computed(() => [{locale:'en',text
<details>
<summary>英文:</summary>
Sorry for writing the code here, without using code snnipet, but I wasn't able to make it work.
I'm developing some generic components for a project. With that in mind I've used a property extraction strategy according to this [post][1]
So I've created an type
```typescript
export declare type ExtractComponentProperties<TComponent> = TComponent extends new () => { $props: infer P }
? P
: never;
Some interfaces to expose properties and emits
import { Nullable } from '@/types/nullable';
import type { ExtractComponentProperties } from '@/types/component/properties/extract';
import type { ExtractComponentEmits } from '@/types/component/emits/extract';
export interface IComponentExtendedProperties<T> {
properties?: ExtractComponentProperties<T>;
}
export interface IComponentProperties {
[key: string]: unknown;
}
export interface IModelValueComponentProperties<T> extends IComponentProperties {
modelValue?: T | Nullable<T>;
}
export interface IComponentExtendedEmits<T> {
emits?: ExtractComponentEmits<T>;
}
Alson created some interfaces that are responsible exposing the extracted values
import { IComponentExtendedProperties } from '@/types/component/properties';
import { IComponentExtendedEmits } from '@/types/component/emits';
export interface IPropertiesBinder<T> extends IComponentExtendedProperties<T> {}
export interface IEmitsBinder<T> extends IComponentExtendedEmits<T> {}
export interface IPropertiesEmitsBinder<T> extends IPropertiesBinder<T>, IEmitsBinder<T> {}
Then I've created a specific Interface foreach component that implements ExtractComponentProperties
import { VMenu } from 'vuetify/lib/components/VMenu/index';
import { VBtn } from 'vuetify/lib/components/VBtn/index';
import { VList } from 'vuetify/lib/components/VList/index';
import { IPropertiesEmitsBinder,IPropertiesBinder } from '@/types/component/binder';
export interface IButton extends IPropertiesEmitsBinder<typeof VBtn> {
text?: string;
}
export interface IMenu extends IComponentExtendedProperties<typeof VMenu> {}
export interface IList extends IPropertiesBinder<typeof VList> {
items: IListItem[];
}
export interface IListItem extends IPropertiesEmitsBinder<typeof VListItem> {
id: string;
title?: string;
}
Then I've created ButtonDropdown.vue sfc component
<template>
<VMenu v-bind="useMenuBinder(componentProperties.menu)">
<template v-slot:activator="{ props }">
<slot name="activator" :props="props">
<VBtn v-bind="useButtonBinder(componentProperties.button, props)">
{{ componentProperties.button?.text }}
<VIcon v-if="componentProperties.button?.properties?.icon" class="ml-2">
{{ componentProperties.button.properties.icon }}
</VIcon>
</VBtn>
</slot>
</template>
<VList v-bind="useListBinder(componentProperties.list)">
<slot>
<VListItem
v-for="(item, index) in availableItems"
:key="index"
:value="item"
v-bind="useListItemBinder(item)"
>
<VListItemTitle v-if="item.title">{{ item.title }}</VListItemTitle>
</VListItem>
</slot>
</VList>
</VMenu>
</template>
<script setup lang="ts">
import { IMenu } from '@/types/component/menu';
import { IList } from '@/types/component/list';
import { IListItem } from '@/types/component/list/item';
import { IButton } from '@/types/component/button';
import { v4 as uuidv4 } from 'uuid';
export interface ButtonDropdownComponentProperties {
menu?: IMenu;
button?: IButton;
list?: IList;
items?: IListItem[];
}
const componentProperties = withDefaults(defineProps<ButtonDropdownComponentProperties>(), {
button: () => ({
properties: {
icon: 'mdi-chevron-down',
color: 'transparent',
size: 'small',
elevation: 0,
},
text: { key: 'buttons.dropdown' },
}),
menu: () => ({
properties: {
maxWidth: '300px',
closeOnContentClick: true,
},
}),
list: () => ({
items: [{ id: generateId(), title: 'Example Text' }],
properties: {
minWidth: '200vw',
maxWidth: 'auto',
},
}),
});
const availableItems = computed(() => componentProperties.list?.items ?? []);
function generateId(): string {
return uuidv4();
}
</script>
Now when i try to call this component
<template>
<ButtonDropdown
:button="{ text: 'Locale', properties: { icon: 'mdi-translate', elevation: '0' } }"
>
<VListItem v-for="(locale, index) in availableLocales">
{{ locale.text }}
</VListItem>
</ButtonDropdown>
</template>
<script setup lang="ts">
export interface ILocale {
locale: string;
text: string;
};
const localeStore = useLocaleStore();
const availableLocales = computed(() => [{locale:'en',text:'English'},{locale:'pt',text:'Portuguese'}]);
const locale = ref<ILocale>({locale:'en',text:'English'});
</script>
Also I've created a composable to help binding the props:
import { IList } from '@/types/component/list';
import { IMenu } from '@/types/component/menu';
import { IButton } from '@/types/component/button';
import { IListItem } from '@/types/component/list/item';
export function useBinder() {
return {
useMenuBinder,
useListItemBinder,
useListBinder,
useButtonBinder,
};
}
export function useMenuBinder(item: IMenu): any {
return { props: item.properties };
}
export function useListItemBinder(item: IListItem) {
return { props: item.properties, emits: item.emits };
}
export function useListBinder(item: IList) {
return { props: item.properties };
}
export function useButtonBinder(item: IButton, options?: any) {
return { props: { ...item.properties }, ...options };
}
As you can see I've passed the elevation property as '0' but its not respecting it;
But If I go directly to the DropdownButton and set :elevation="componentProperties.button.properties?.elevation"
it works
What I'm missing?
答案1
得分: 0
我刚刚发现发生了什么
总结一下:
问题在于我正在绑定一个具有属性的对象,但这些属性不存在
export function useButtonBinder(item: IButton, options?: any) {
return { props: { ...item.properties }, ...options };
}
正确的方法是:
export function useButtonBinder(item: IButton, options?: any) {
return { ...item.properties , ...options };
}
这样属性将被分配到正确的值中
英文:
I've just found out what was happening
Looking into developer tools i noticed that the bind wasn't right
To summarize it:
The problem was that i was binding an object with props, which doesn't exists
export function useButtonBinder(item: IButton, options?: any) {
return { props: { ...item.properties }, ...options };
}
The right way to do this is:
export function useButtonBinder(item: IButton, options?: any) {
return { ...item.properties , ...options };
}
This way the properties will be assigned to the correct values
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论