vue.js 在开槽子组件中改变数据的正确方法

pepwfjgg  于 2022-11-17  发布在  Vue.js
关注(0)|答案(2)|浏览(150)

我想创建一个由三部分组成的表组件: Package 、头和数据。
虽然它的大部分工作得很好,我挣扎的顺序功能。
当用户点击一个th标签时,数据应该被重新排序,并且应该显示一个小的指示器。排序起作用,但是指示器不起作用。

真正的问题

我知道在父对象中定义了一个属性,但在子对象中改变它是不好的,因为我使用了slot,所以不能使用普通的$emit。
使用这里显示的方法,我得到了Uncaught (in promise) TypeError: parent is nullUnhandled error during execution of scheduler flush
虽然我已经知道我目前的方法是错误的,但我不知道如何做正确的。
在谷歌上搜索,我发现了一些关键词,比如可写的计算道具和作用域插槽,但我不能把它们放在一起。
那么,在一个插槽环境中实现双向绑定的正确方法是什么呢?

我的表.vue文件

<template>
    <div class="px-4 sm:px-6 lg:px-8">
        <div class="mt-8 flex flex-col">
            <div class="-my-2 -mx-4 overflow-x-auto sm:-mx-6 lg:-mx-8">

                <Search v-if="searchable" :filters="props.filters"></Search>

                <div class="inline-block min-w-full py-2 align-middle">
                    <div class="overflow-hidden shadow ring-1 ring-black ring-opacity-5 md:rounded-lg">
                        <table class="min-w-full divide-y divide-gray-300">
                            <thead class="bg-gray-50">
                            <tr>
                                <slot name="table-heads"></slot>
                            </tr>
                            </thead>
                            <tbody class="divide-y divide-gray-200 bg-white">
                                <slot name="table-body"></slot>
                            </tbody>
                        </table>

                        <Pagination v-if="paginationLinks" :links="paginationLinks"></Pagination>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script setup>
import Pagination from "@/Components/Tables/Pagination.vue";
import {provide} from "vue";
import Search from "@/Components/Tables/Search.vue";
import {Inertia} from "@inertiajs/inertia";

    let name = "Table";

    let props = defineProps({
        paginationLinks: Array,
        dataUrl: String,
        filters: Object,
        order: Object,
        searchable: Boolean
    });

    provide('dataUrl', props.dataUrl);
    provide('order', props.order);

</script>

<style scoped>

</style>

我的TableHead.vue文件

<template>
    <th @click="orderByClicked" scope="col"
        class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6 cursor-pointer">
        <div class="flex justify-between">
            <slot></slot>
            <span v-if="order.orderBy === props.orderKey">
                <i v-if="order.orderDirection === 'asc'" class="fa-solid fa-chevron-up"></i>
                <i v-if="order.orderDirection === 'desc'" class="fa-solid fa-chevron-down"></i>
            </span>
        </div>
    </th>
</template>

<script setup>
import { inject } from "vue";
import { Inertia } from "@inertiajs/inertia";

let name = "TableHead";
let dataUrl = inject('dataUrl');
let order = inject('order');

let props = defineProps({
    orderKey: String,
    orderByClicked: Function
});

function orderByClicked() {
    if (props.orderKey) {
        if (order.orderBy === props.orderKey)
            order.orderDirection = order.orderDirection === 'asc' ? 'desc' : 'asc';
        else
            order.orderDirection = "asc"

        order.orderBy = props.orderKey;

        Inertia.get(dataUrl, {orderBy: props.orderKey, orderDirection: order.orderDirection}, {
            preserveState: true,
            replace: true
        });
    }
}

</script>

<style scoped>

</style>

我的TableData.vue文件(即将完成)

<template>
    <td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6">
        <slot></slot>
    </td>
</template>

<script setup>
    let name = "TableData";
</script>

<style scoped>

</style>

"把它放在一起"

<Table :pagination-links="props.users.links" :data-url="'/users'" :searchable="true" :filters="props.filters" :order="props.order">
                <template #table-heads>
                    <TableHead order-key="name">name</TableHead>
                    <TableHead order-key="email">email</TableHead>
                    <TableHead>Bearbeiten</TableHead>
                </template>

                <template #table-body>
                    <tr v-for="user in users.data" :key="user.id">
                        <TableData>{{ user.username }}</TableData>
                        <TableData>{{ user.email}}</TableData>
                    </tr>
                </template>
            </Table>
oyt4ldly

oyt4ldly1#

这可能是一个React性问题。您的指标依赖于order属性,您在设置时设置了该属性,并且似乎更改正确,但我没有看到任何东西使它对您的模板React。
在您提供它的Table.vue中,您可能只需要从Vue导入ref以使其具有React性:

import { ref, provide } from 'vue'

// provide static value
provide('dataUrl', props.dataUrl);

// provide reactive value
const order = ref(props.order)
provide('order', order);
eqqqjvef

eqqqjvef2#

我意识到错误只发生在与FontAwesome结合的情况下,所以我看得更远。
通过检查代码,我发现FontAwesome操纵DOM。
事后应该很清楚,但无论如何...
"原因"
关键是,当您插入<i class="fa-solid fa-sort-up"></i>这样的标签时,它会转换为<svg class="svg-inline--fa fa-sort-up" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="sort-up" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512" data-fa-i2svg=""><path fill="currentColor" d="M182.6 41.4c-12.5-12.5-32.8-12.5-45.3 0l-128 128c-9.2 9.2-11.9 22.9-6.9 34.9s16.6 19.8 29.6 19.8H288c12.9 0 24.6-7.8 29.6-19.8s2.2-25.7-6.9-34.9l-128-128z"></path></svg>
只要你不把自己的属性附加到i标签上就行了,因为它们会被破坏!
我的v-if也发生了这种情况。不幸的是,它并没有悄无声息地消失,而是导致了一些奇怪的错误。

解决方案

将任何由第三方(在本例中为FontAwesome)操作的标签 Package 在一个合适的父标签中。
我想出了以下解决办法:

<span v-if="someCondition">
   <i class="fa-solid fa-sort-up"></i>
</span>

琐事

有趣的是,由于某些原因,这个错误并没有在https://sfc.vuejs.org中出现。我无法在任何在线工具中重现它。组件只在我的本地开发环境中崩溃。

相关问题