vue3(自定义组件) prop 和 Attrs的使用方式(父子组件双向绑定)

效果:

        1.单后缀

        2.多后缀

版本:

        "vue": "^3.2.47",

        "element-plus": "^2.3.6",

需求:

        样式中类似 height: 100px;有很多这种带单位样式,那么需要做一个填值,然后选择后缀的element 组件。
        实际就是一个原生字段,对应多个修改字段。

问题说明:

        使用props,子组件使用v-model绑定后,可以传值给子组件,但是子组件不能修改。
        使用attrs,可以传值给子组件,可以修改但是父组件的源数据不会一起变化且源数据变化,它也不会变化。

        如果子组件需要影响源数据,需要使用emits。

解决方式:

        1.父子组件都绑定同一个源数据,比如pinia。(这种不能叫组件且耦合)

        2.prop接收源数据,attrs接收其他信息,然后使用emits来更改源数据。

例如:element 后缀组件

        API:

                

属性名称作用例子

data

源数据v-model:data="inputS"

suffix

后缀。string | Array

例如:"suffix",["suffix1","suffix2"],[["suffixLabel","suffix"],["suffixLabel","suffix2"]]

inputClick

输入时就同步数据给源数据,否则就是输入框change同步数据

:valueAttrs="valueAttrs"

 

const valueAttrs = {
dataXXX: '500px',
style: {
backgroundColor: 'red'
}
}

valueAttrs

值输入框的属性

suffixAttrs

后缀的属性同valueAttrs

       使用:

<template>
    <div>
        <InputSuffix2 v-model:data="inputS" :suffix="['px', '%']"/>
    </div>

</template>

<script setup>
import InputSuffix2 from "@component/element/input_suffix2.vue";
</script>

组件代码:

<template>
    <div class="custom-com-input-suffix" v-bind="attrs.divAttrs">
        <template v-if="attrs.inputClick !== undefined">
            <el-input @input="valueChange" v-model="input" style="width: 65%;" v-bind="attrs.valueAttrs"/>
            <template v-if="typeof attrs.suffix === 'string'">
                <el-input @input="suffixChange" v-model="suffix" type="text" style="width: 35%;"
                          v-bind="attrs.suffixAttrs"/>
            </template>
        </template>
        <template v-else>
            <el-input @change="valueChange" v-model="input" style="width: 65%;" v-bind="attrs.valueAttrs"/>
            <template v-if="typeof attrs.suffix === 'string'">
                <el-input @change="suffixChange" v-model="suffix" type="text" style="width: 35%;"
                          v-bind="attrs.suffixAttrs"/>
            </template>
        </template>
        <template v-if="Array.isArray(attrs.suffix)">
            <el-select @change="suffixChange" v-model="suffix" style="width: 35%;" v-bind="attrs.suffixAttrs"
                       placeholder=" ">
                <template v-for="obj in attrs.suffix">
                    <template v-if="typeof obj === 'string'">
                        <el-option :label="obj" :value="obj"/>
                    </template>
                    <template v-else>
                        <template v-if="Array.isArray(obj)">
                            <el-option :label="obj[0]" :value="obj[1]"/>
                        </template>
                        <template v-else>
                            <el-option :label="obj.label" :value="obj.value"/>
                        </template>
                    </template>
                </template>
            </el-select>
        </template>
    </div>
</template>

<script setup>
import {ref, useAttrs, watch} from "vue";

const attrs = useAttrs()
const suffix = ref('')
const input = ref('')

const props = defineProps({
    data: String
})

watch(props, (val, preVal) => {
    setValueAndSuffix(props.data)
})

const setValueAndSuffix = (val) => {
    const exceptionStr = '后缀目前只支持字符串和数组,例如:"suffix",["suffix1","suffix2"],[["suffixLabel","suffix"],["suffixLabel","suffix2"]]'
    if (typeof attrs.suffix === 'string') {
        if (val.indexOf(attrs.suffix) > -1) {
            input.value = val.split(suffix.value)[0]
            suffix.value = attrs.suffix
        }
    } else if (Array.isArray(attrs.suffix)) {
        for (let i = 0; i < attrs.suffix.length; i++) {
            if (typeof attrs.suffix[i] === 'string') {
                if (val.indexOf(attrs.suffix[i]) > -1) {
                    input.value = val.split(attrs.suffix[i])[0]
                    suffix.value = attrs.suffix[i]
                    break
                }
            } else if (Array.isArray(attrs.suffix[i])) {
                if (val.indexOf(attrs.suffix[i]) > -1) {
                    input.value = val.split(attrs.suffix[i])[0]
                    suffix.value = attrs.suffix[i][1]
                    break
                }
            } else {
                new TypeError(exceptionStr)
            }
        }
    } else {
        new TypeError(exceptionStr)
    }
}

setValueAndSuffix(props.data)

const emits = defineEmits(['update:data'])

const valueChange = () => {
    emits('update:data', input.value + suffix.value)
}

const suffixChange = () => {
    emits('update:data', input.value + suffix.value)
}

</script>

<style scoped>
.custom-com-input-suffix {
    display: inline-block;
}
</style>

        

例子:版本v2
支持直接type定制,使用element的后缀方式,前缀也可以类似实现

使用:

<InputSuffix type="number" v-model:data="inputS" :suffix="POSITION" inputClick/>

<template>
    <div class="custom-com-input-suffix" v-bind="attrs.divAttrs">
        <template v-if="attrs.inputClick !== undefined">
            <el-input :type="inputType" @input="valueChange" v-model="input" v-bind="attrs.valueAttrs">
                <template #append>
                    <template v-if="typeof attrs.suffix === 'string'">
                        <el-input @input="suffixChange" v-model="suffix" type="text"
                                  v-bind="attrs.suffixAttrs"/>
                    </template>
                    <template v-if="Array.isArray(attrs.suffix)">
                        <el-select @change="suffixChange" v-model="suffix"
                                   v-bind="attrs.suffixAttrs"
                                   placeholder=" ">
                            <template v-for="obj in attrs.suffix">
                                <template v-if="typeof obj === 'string'">
                                    <el-option :label="obj" :value="obj"/>
                                </template>
                                <template v-else>
                                    <template v-if="Array.isArray(obj)">
                                        <el-option :label="obj[0]" :value="obj[1]"/>
                                    </template>
                                    <template v-else>
                                        <el-option :label="obj.label" :value="obj.value"/>
                                    </template>
                                </template>
                            </template>
                        </el-select>
                    </template>
                </template>
            </el-input>
        </template>
        <template v-else>
            <el-input :type="inputType" @change="valueChange" v-model="input" v-bind="attrs.valueAttrs">
                <template #append>
                    <template v-if="typeof attrs.suffix === 'string'">
                        <el-input @change="suffixChange" v-model="suffix" type="text"
                                  v-bind="attrs.suffixAttrs"/>
                    </template>
                    <template v-if="Array.isArray(attrs.suffix)">
                        <el-select @change="suffixChange" v-model="suffix"
                                   v-bind="attrs.suffixAttrs"
                                   placeholder=" ">
                            <template v-for="obj in attrs.suffix">
                                <template v-if="typeof obj === 'string'">
                                    <el-option :label="obj" :value="obj"/>
                                </template>
                                <template v-else>
                                    <template v-if="Array.isArray(obj)">
                                        <el-option :label="obj[0]" :value="obj[1]"/>
                                    </template>
                                    <template v-else>
                                        <el-option :label="obj.label" :value="obj.value"/>
                                    </template>
                                </template>
                            </template>
                        </el-select>
                    </template>
                </template>
            </el-input>
        </template>
    </div>
</template>

<script setup>
import {ref, useAttrs, watch} from "vue";

const attrs = useAttrs()
const suffix = ref('')
const input = ref('')
const inputType = ref('type')

const props = defineProps({
    data: String
})

watch(props, (val, preVal) => {
    setValueAndSuffix(props.data)
})

if (attrs.type && attrs.type.length > 0) {
    inputType.value = attrs.type
}

const setValueAndSuffix = (val) => {
    const exceptionStr = '后缀目前只支持字符串和数组,例如:"suffix",["suffix1","suffix2"],[["suffixLabel","suffix"],["suffixLabel","suffix2"]]'
    if (typeof attrs.suffix === 'string') {
        if (val.indexOf(attrs.suffix) > -1) {
            input.value = val.split(suffix.value)[0]
            suffix.value = attrs.suffix
        }
    } else if (Array.isArray(attrs.suffix)) {
        for (let i = 0; i < attrs.suffix.length; i++) {
            if (typeof attrs.suffix[i] === 'string') {
                if (val.indexOf(attrs.suffix[i]) > -1) {
                    input.value = val.split(attrs.suffix[i])[0]
                    suffix.value = attrs.suffix[i]
                    break
                }
            } else if (Array.isArray(attrs.suffix[i])) {
                if (val.indexOf(attrs.suffix[i]) > -1) {
                    input.value = val.split(attrs.suffix[i])[0]
                    suffix.value = attrs.suffix[i][1]
                    break
                }
            } else {
                new TypeError(exceptionStr)
            }
        }
    } else {
        new TypeError(exceptionStr)
    }
}

setValueAndSuffix(props.data)

const emits = defineEmits(['update:data'])

const valueChange = () => {
    emits('update:data', input.value + suffix.value)
}

const suffixChange = () => {
    emits('update:data', input.value + suffix.value)
}

</script>

<style scoped>
.custom-com-input-suffix {
    display: inline-block;
}
</style>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue 3中,父子组件之间的双向绑定可以通过v-model指令实现。下面是一个简单的示例: 父组件: ```html <template> <div> <h2>父组件</h2> <input v-model="message" placeholder="输入内容"> <p>组件传递的值:{{ childMessage }}</p> <ChildComponent v-model="childMessage"></ChildComponent> </div> </template> <script> import ChildComponent from './ChildComponent.vue'; export default { components: { ChildComponent }, data() { return { message: '', childMessage: '' }; } }; </script> ``` 组件 (ChildComponent.vue): ```html <template> <div> <h3>组件</h3> <input v-model="localValue" placeholder="输入内容"> <button @click="updateParentValue">更新父组件值</button> </div> </template> <script> export default { model: { prop: 'value', event: 'input' }, props: { value: { type: String, default: '' } }, data() { return { localValue: this.value }; }, methods: { updateParentValue() { this.$emit('input', this.localValue); } } }; </script> ``` 在父组件中,我们使用v-model指令将`message`变量与组件的`childMessage`进行双向绑定。在组件中,我们将`v-model`绑定到本地的`localValue`,并通过`updateParentValue`方法来触发`input`事件,将`localValue`值传递给父组件。 这样,父组件组件之间的值就可以实现双向绑定了。当父组件的`message`变化时,组件的`localValue`也会跟着变化;当组件的值变化时,会通过`input`事件将新值传递给父组件的`childMessage`。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值