Vue 2 to 3升级:简化复选框问题组件

问题描述 投票:0回答:1

我有一个在 Vue 2 中制作的复选框表单问题组件,我正在迁移到 Vue 3,因此希望尽可能简化它,并确保我已经利用了 Vue 3 的所有新功能。

经过大量的尝试和错误,我已经设法让它工作,但我是否过于复杂了?

该组件应使用 v-slot 提出问题并在“options”数组属性中给出答案。 它应该让用户选择尽可能多的适用答案,但如果他们选择“无”,则应该清除所有其他答案。 如果他们选择了“无”,那么他们会选择另一个答案,它应该清除“无”,这样就不再检查它。

父组件

<template>
    <div>
        <h2>title</h2>

        <InputCheckboxGroup
            name="question_one"
            :required="checkIsRequired('question_one')"
            :error="formRef.errors.get('question_one')"
            v-model="formRef.question_one"
            :options="getField('question_one')['options']"
            @update:modelValue="handleAffectedByInput"
        >
            <template v-slot:question>
                {{ getField("question_one").question }}
            </template>
        
        </InputCheckboxGroup>

    </div>
</template>

<script setup>
import InputCheckboxGroup from "../Form/InputCheckboxGroup";
import Form from "../../../../Form";
import { isRequired } from "./formHelper.js";
import { ref, markRaw, defineExpose } from "vue";

const props = defineProps({
    step: Object,
    fields: Object,
    formData: Object
})


let formRef = markRaw(ref(new Form({
    question_one: []
}, props.formData)))

const getField = (fieldName) => {
   return props.fields[fieldName];
}

const checkIsRequired = (fieldName) => {
   var fieldData = getField(fieldName);
   return isRequired(fieldData, formRef.value);
}

function handleAffectedByInput(values) {
    if (values.length && (values[values.length - 1] === 'none')) {
        formRef.question_one = [values[values.length - 1]];
        return;
    }
   clearCheckboxValues(['none'], values, 'question_one');
}

function clearCheckboxValues(previousAnswers, values, formField) { 
    for (const answer of previousAnswers) {
        if (values.includes(answer)) {
            formRef.value[formField] = values.filter(value => value !== answer);
        }
    }
}

defineExpose({
 formRef
})
</script>

子/复选框问题组件

<template>
    <div class="form-group">
        <fieldset :aria-describedby="name">
            <legend>
                <p v-if="$slots.question" class="input__question">
                    <slot name="question"></slot>
                </p>
            </legend>
            <div
                class="input_checkbox"
                v-for="opt in options"
                v-bind:key="opt.value"  
            >           
                <label v-if="opt.value == 'none'">
                    <input
                        type="checkbox"
                        value="none"
                        v-model="model"
                        @click="onCheckboxChange"
                    />
                    <span>None</span>
                </label>    
                <div v-else class="form-group form-check">
                    <input  
                        type="checkbox"
                        class="form-check-input"
                        :value="opt.value"
                        @click="onCheckboxChange"
                        v-model="model"
                    />
                    <label :for="opt.value" class="form-check-label">
                        {{ opt.label }}
                    </label>    
                </div>
            </div>
        </fieldset>
    </div>
</template>
  
<script setup>
import { defineModel, defineEmits } from "vue";
const props = defineProps({
    error: String,
    name: String,
    options: Array,
    required: Boolean
});

const model = defineModel()

const emit = defineEmits(['update:modelValue'])

function optionIsChecked (value) {
    return model.value.includes(value);
}

function onCheckboxChange($event) {
    var previouslySelected = model.value || [];
    var newValue = [];
    if ($event.target.checked) {
        newValue = [...previouslySelected, $event.target.value];
    } else {
        newValue = previouslySelected.filter(
            (x) => x != $event.target.value
        );
    }
    
    if ($event.target.value === 'none' || $event.target.value === 'involved_none_above') {
        newValue = [$event.target.value];
    }
    
    model.value = newValue
    
    emit("update:modelValue", newValue);
}

</script>
javascript vue.js vuejs3 vue-component
1个回答
0
投票

以最简单的形式,您所追求的内容如下:

const { createApp, ref, computed, watch } = Vue

createApp({
  setup() {
    const options = ['one', 'two', 'three', 'four']
    const selection = ref([])
    const isNoneSelected = computed({
      get() {
        return !selection.value.length
      },
      set() {
        selection.value = []
      }
    })
    return {
      options,
      selection,
      isNoneSelected
    }
  }
}).mount('#app')
label input[type="radio"] {
  pointer-events: none;
}
label {
  display: block;
  cursor: pointer;
}
label:hover {
  background-color: #f5f5f5;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.5.4/vue.global.prod.min.js"></script>
<div id="app">
  <label v-for="item in options" :key="item">
    <input type="checkbox" v-model="selection" :value="item" />{{ item }}
  </label>
  <label>
    <input type="checkbox" v-model="isNoneSelected" /> none
  </label>
</div>

您也可以使用

reactive
代替
ref
:

const { reactive, toRefs } = Vue

//...
setup() {
  const state = reactive({
    options: ['one', 'two', 'three', 'four'],
    selection: [],
    isNoneSelected: computed({
      get() {
        return !state.selection.length
      },
      set() {
        state.selection = []
      }
    })
  })
  return toRefs(state)
}

确实,同一件事有不同的语法。模板保持不变。

© www.soinside.com 2019 - 2024. All rights reserved.