首页  >  问答  >  正文

vue 3 组合 api 组件,带有复选框数组并切换所有

<p>我们决定逐渐从淘汰赛转向使用 typescript 的 vue 3 组合 api,并且我正在尝试了解变异道具的反模式。我有一个正在执行其预期工作的工作组件,但基本上我想确认它的编写方式是建议的方法。</p> <p>一个相当简单的例子是一个复选框列表组件,上面有一个切换开关:</p> <p>我最大的问题是我在 AppList.vue 中所做的是否正确,我正在做 <code>const internalModel = toRef(props.selected ?? []);</code> 以获得不可变的组件中的变量和 <code>selectedHandler</code> - 事件和 <code>toggleAll</code> - 计算为发出 OUT,但在这里我手动保留 <code>selected</code> 和 <代码>内部模型</代码>同步。对于同一件事使用两个变量感觉很麻烦,但同时它确实有意义,因为内部模型不需要干扰视图。</p> <p>我知道 vuejs.org 上有一个示例,其中可以在 <code>v-model</code> 上使用数组来表示多个复选框,但它不在组件内部或作为道具,因此它不完全是同样,这感觉更复杂。我花了一天中的大部分时间试图让它正确,但没有那么多 vue 3 搜索结果,对于这个特定问题我根本没有找到任何结果。</p> <p>HomeView.vue:</p> <p> <pre class="brush:html;toolbar:false;"><script set lang="ts"> import { ref } from 'vue'; import AppList, { type Item } from '@/components/AppList.vue'; const fooItems = ref<Item[]>([ { id: 1, name: 'foo 1' }, { id: 2, name: 'foo 2' }, { id: 3, name: 'foo 3' }, { id: 4, name: 'foo 4' }, { id: 5, name: 'foo 5' }, ]); const fooSelected = ref<number[]>([]); </script> <template> <AppList :items="fooItems" v-model:selected="fooSelected"></AppList> <div>fooselected: {{ fooSelected }}</div> </模板></pre> </p> <p>组件/Applist.vue:</p> <p>
<脚本设置lang="ts">
从“vue”导入{计算,toRef};

导出接口项{
    身份证号;
    名称:字符串;
}

const 道具 = DefineProps<{
    项目:项目[];
    已选择?:编号[];
}>();

const internalModel = toRef(props.selected ?? []);

const 发出 = DefineEmits<{
    '更新:选定':[选定:数字[]];
}>();

const selectedHandler = (e: 事件) => {
    const target = e.target;
    if (props.selected && target) {
        if (目标.检查) {
            发出('更新:选定',[...props.selected,Number(目标.值)]);
        } 别的 {
            发射(
                '更新:已选择',
                props.selected.filter((i: number) => i !== Number(target.value))
            );
        }
    }
};

consttoggleAll = 计算({
    得到:() => InternalModel.value.length === props.items.length && InternalModel.value.every((s) => props.items.map((item) => item.id).includes(s)),
    设置:(值)=> {
        如果(值){
            发射(
                '更新:已选择',
                props.items.map((i) => i.id)
            );
            InternalModel.value = props.items.map((i) => i.id);
        } 别的 {
            发出('更新:所选',[]);
            内部模型.value = [];
        }
    },
});
</脚本>

<模板>
    <标签>
        <输入类型=“复选框”v-model=“toggleAll”/>
        全部切换
    </标签>
    
  • <标签> > id {{ item.name }}; </标签> </li> </ul> 内部模型:{{内部模型}} </模板></pre> </p>
P粉670838735P粉670838735415 天前597

全部回复(1)我来回复

  • P粉203792468

    P粉2037924682023-08-31 15:28:23

    在我看来,这可以以某种更简单的方式完成。
    fooItems 可能应该有一个初始状态“已检查”。
    selectedHandler中,只需调用emit()即可。
    toggleAll 最终将创建一个与 internalModel 配合使用的函数。
    这是一个示例 Vue SFC Playground。< /p>


    HomeView.vue:

    <script setup lang="ts">
    import { ref } from 'vue';
    import AppList, { type Item } from './AppList.vue';
    
    const fooItems = ref<Item[]>([
      { id: 1, name: 'foo 1', checked: false },
      { id: 2, name: 'foo 2', checked: false },
      { id: 3, name: 'foo 3', checked: false },
      { id: 4, name: 'foo 4', checked: false },
      { id: 5, name: 'foo 5', checked: true },
    ]);
    const fooSelected = ref<number[]>([]);
    fooItems.value.map(item => item.checked && fooSelected.value.push(item.id))
    </script>
    
    <template>
      <AppList :items="fooItems" v-model:selected="fooSelected"></AppList>
      <div>fooselected: {{ fooSelected }}</div>
    </template>

    AppList.view:

    <script setup lang="ts">
    import { ref } from 'vue';
    
    export interface Item {
      id: number;
      name: string;
      checked: boolean
    }
    
    const props = defineProps<{
      items: Item[];
      selected: number[]
    }>();
    
    const emit = defineEmits(['update:selected']);
    
    const internalModel = ref(props.selected);
      
    const selectedHandler = () => emit('update:selected', internalModel.value);
    
    const toggleAll = ($event) => {
      internalModel.value = [];
      if ( ($event.target as HTMLInputElement).checked ) {
        props.items.map(item => internalModel.value.push(item.id));
      }
      emit('update:selected', internalModel.value);
    };
    </script>
    
    <template>
      <label>
        <input type="checkbox" @change="toggleAll($event)" :checked="internalModel.length === items.length" />
        toggle all
      </label>
      <ul>
        <li v-for="item in items" :key="item.id">
          <label>
            <input type="checkbox" :value="item.id" v-model="internalModel" @change="selectedHandler(item.id)" :checked="item.checked"/>
            <span>{{ item.name }}</span>
          </label>
        </li>
      </ul>
      internalModel: {{ internalModel }}
    </template>

    回复
    0
  • 取消回复