Vuetify3- Vue3 V-Bind没有按预期工作

huangapple go评论79阅读模式
英文:

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&#39;t able to make it work.

I&#39;m developing some generic components for a project. With that in mind I&#39;ve used a property extraction strategy according to this [post][1]




So I&#39;ve created an type 
```typescript
export declare type ExtractComponentProperties&lt;TComponent&gt; = TComponent extends new () =&gt; { $props: infer P }
	? P
	: never;

Some interfaces to expose properties and emits

import { Nullable } from &#39;@/types/nullable&#39;;
import type { ExtractComponentProperties } from &#39;@/types/component/properties/extract&#39;;
import type { ExtractComponentEmits } from &#39;@/types/component/emits/extract&#39;;
export interface IComponentExtendedProperties&lt;T&gt; {
	properties?: ExtractComponentProperties&lt;T&gt;;
}
export interface IComponentProperties {
	[key: string]: unknown;
}
export interface IModelValueComponentProperties&lt;T&gt; extends IComponentProperties {
	modelValue?: T | Nullable&lt;T&gt;;
}

export interface IComponentExtendedEmits&lt;T&gt; {
	emits?: ExtractComponentEmits&lt;T&gt;;
}


Alson created some interfaces that are responsible exposing the extracted values

import { IComponentExtendedProperties } from &#39;@/types/component/properties&#39;;
import { IComponentExtendedEmits } from &#39;@/types/component/emits&#39;;

export interface IPropertiesBinder&lt;T&gt; extends IComponentExtendedProperties&lt;T&gt; {}
export interface IEmitsBinder&lt;T&gt; extends IComponentExtendedEmits&lt;T&gt; {}
export interface IPropertiesEmitsBinder&lt;T&gt; extends IPropertiesBinder&lt;T&gt;, IEmitsBinder&lt;T&gt; {}

Then I've created a specific Interface foreach component that implements ExtractComponentProperties

import { VMenu } from &#39;vuetify/lib/components/VMenu/index&#39;;
import { VBtn } from &#39;vuetify/lib/components/VBtn/index&#39;;
import { VList } from &#39;vuetify/lib/components/VList/index&#39;;
import { IPropertiesEmitsBinder,IPropertiesBinder } from &#39;@/types/component/binder&#39;;
export interface IButton extends IPropertiesEmitsBinder&lt;typeof VBtn&gt; {
	text?: string;
}


export interface IMenu extends IComponentExtendedProperties&lt;typeof VMenu&gt; {}
export interface IList extends IPropertiesBinder&lt;typeof VList&gt; {
	items: IListItem[];
}
export interface IListItem extends IPropertiesEmitsBinder&lt;typeof VListItem&gt; {
	id: string;
	title?: string;
}

Then I've created ButtonDropdown.vue sfc component

&lt;template&gt;
	&lt;VMenu v-bind=&quot;useMenuBinder(componentProperties.menu)&quot;&gt;
		&lt;template v-slot:activator=&quot;{ props }&quot;&gt;
			&lt;slot name=&quot;activator&quot; :props=&quot;props&quot;&gt;
				&lt;VBtn v-bind=&quot;useButtonBinder(componentProperties.button, props)&quot;&gt;
					{{ componentProperties.button?.text }}
					&lt;VIcon v-if=&quot;componentProperties.button?.properties?.icon&quot; class=&quot;ml-2&quot;&gt;
						{{ componentProperties.button.properties.icon }}
					&lt;/VIcon&gt;
				&lt;/VBtn&gt;
			&lt;/slot&gt;
		&lt;/template&gt;
		&lt;VList v-bind=&quot;useListBinder(componentProperties.list)&quot;&gt;
			&lt;slot&gt;
				&lt;VListItem
					v-for=&quot;(item, index) in availableItems&quot;
					:key=&quot;index&quot;
					:value=&quot;item&quot;
					v-bind=&quot;useListItemBinder(item)&quot;
				&gt;
					&lt;VListItemTitle v-if=&quot;item.title&quot;&gt;{{ item.title }}&lt;/VListItemTitle&gt;
				&lt;/VListItem&gt;
			&lt;/slot&gt;
		&lt;/VList&gt;
	&lt;/VMenu&gt;
&lt;/template&gt;
&lt;script setup lang=&quot;ts&quot;&gt;
import { IMenu } from &#39;@/types/component/menu&#39;;
import { IList } from &#39;@/types/component/list&#39;;
import { IListItem } from &#39;@/types/component/list/item&#39;;
import { IButton } from &#39;@/types/component/button&#39;;
import { v4 as uuidv4 } from &#39;uuid&#39;;
export interface ButtonDropdownComponentProperties {
	menu?: IMenu;
	button?: IButton;
	list?: IList;
	items?: IListItem[];
}
const componentProperties = withDefaults(defineProps&lt;ButtonDropdownComponentProperties&gt;(), {
	button: () =&gt; ({
		properties: {
			icon: &#39;mdi-chevron-down&#39;,
			color: &#39;transparent&#39;,
			size: &#39;small&#39;,
			elevation: 0,
		},
		text: { key: &#39;buttons.dropdown&#39; },
	}),
	menu: () =&gt; ({
		properties: {
			maxWidth: &#39;300px&#39;,
			closeOnContentClick: true,
		},
	}),
	list: () =&gt; ({
		items: [{ id: generateId(), title: &#39;Example Text&#39;  }],
		properties: {
			minWidth: &#39;200vw&#39;,
			maxWidth: &#39;auto&#39;,
		},
	}),
});
const availableItems = computed(() =&gt; componentProperties.list?.items ?? []);
function generateId(): string {
	return uuidv4();
}
&lt;/script&gt;

Now when i try to call this component

&lt;template&gt;
	&lt;ButtonDropdown
		:button=&quot;{ text: &#39;Locale&#39;, properties: { icon: &#39;mdi-translate&#39;, elevation: &#39;0&#39; } }&quot;
	&gt;
		&lt;VListItem v-for=&quot;(locale, index) in availableLocales&quot;&gt;
			{{ locale.text }}
		&lt;/VListItem&gt;
	&lt;/ButtonDropdown&gt;
&lt;/template&gt;
&lt;script setup lang=&quot;ts&quot;&gt;
export interface ILocale {
	locale: string;
	text: string;
};
const localeStore = useLocaleStore();
const availableLocales = computed(() =&gt; [{locale:&#39;en&#39;,text:&#39;English&#39;},{locale:&#39;pt&#39;,text:&#39;Portuguese&#39;}]);
const locale = ref&lt;ILocale&gt;({locale:&#39;en&#39;,text:&#39;English&#39;});
&lt;/script&gt;

Also I've created a composable to help binding the props:

import { IList } from &#39;@/types/component/list&#39;;
import { IMenu } from &#39;@/types/component/menu&#39;;
import { IButton } from &#39;@/types/component/button&#39;;
import { IListItem } from &#39;@/types/component/list/item&#39;;

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=&quot;componentProperties.button.properties?.elevation&quot; it works

What I'm missing?

答案1

得分: 0

我刚刚发现发生了什么

查看开发者工具时,我注意到绑定不正确
Vuetify3- Vue3 V-Bind没有按预期工作

总结一下:

问题在于我正在绑定一个具有属性的对象,但这些属性不存在

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
Vuetify3- Vue3 V-Bind没有按预期工作

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

huangapple
  • 本文由 发表于 2023年1月9日 15:07:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/75054063.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定