美文网首页web前端
自定义radio-group组件

自定义radio-group组件

作者: 姜治宇 | 来源:发表于2020-10-02 11:31 被阅读0次

如果radio只有少量的几个,我们完全可以每个都绑v-model,但如果radio太多的话,代码就显得啰嗦了,我们用radio-group分组来实现。
我们先定义一下radio-group。
group组件功能比较简单,就是把里面的内容用slot完全展示出来。
components/myradiogroup.vue:

<template>
    <div>
        <slot></slot>
    </div>
</template>

<script>
    export default {
        name: "MyRadioGroup",
        props:{
            value:null //接收v-model
        }
    }
</script>

然后注册一下。
main.js:

import Vue from 'vue'
import App from './App.vue'
import MyRadio from './components/myradio'
import MyRadioGroup from './components/myradiogroup'

Vue.config.productionTip = false

Vue.component(MyRadio.name,MyRadio)
Vue.component(MyRadioGroup.name,MyRadioGroup)
new Vue({
  render: h => h(App),
}).$mount('#app')

使用起来也比较简单,就是把原来绑定在my-radio上面的v-model,提到了my-radio-group组件上面来。
App.vue

<template>
  <div id="app">

    <my-radio-group v-model="sex">
      <my-radio label="0">男</my-radio>
      <my-radio label="1">女</my-radio>
    </my-radio-group>

  </div>
</template>
<script>
  export default {
    data(){
      return {
        sex: '0'
      }
    }
  }
</script>

这就会出现一个问题:
group组件接收到的v-model值,如何传递到里面的radio组件?
我们很自然的想到了$parent,在radio组件通过$parent可以访问到v-model。但是,这种严格的层级关系容易出问题,比如这样的:

<template>
  <div id="app">
    <my-radio-group v-model="sex">
      <my-button>
          <my-radio label="0">男</my-radio>
          <my-radio label="1">女</my-radio>
      </my-button>
    </my-radio-group>
  </div>
</template>

这种层级关系会让$parent失去作用,我们可以用provide-inject方案来解决。
provide-inject是多层级组件之间信息通信的一种方案,类似我们常用的vuex。
但vuex需额外定义store等好多东西,与具体项目耦合度太高,显然不适合自定义组件场景,因此provide-inject就是最佳替代方案。
我们在group组件定义provide,注意provide跟data属性一样,需使用回调函数。
components/myradiogroup.vue:

<template>
    <div>
       <slot></slot>
    </div>
</template>

<script>
    export default {
        name: "MyRadioGroup",
        provide(){
            return {
                radioGroup: this
            }
        },
        props:{
            value:null //接收v-model
        }
    }
</script>

我们定义了一个新变量radioGroup,值就是当前这个group组件。
也就是说,我们将group组件整个往下传递,这样在子组件radio(或者更深层次的组件)需要什么,就直接从group组件取就可以了。
components/myradio.vue:

<template>
    <label class="my-radio" :class="{'is-checked': label === model}">

        <span class="my-radio__input">
            <span class="my-radio__inner"></span>
            <!--真实的radio,被隐藏,去掉.my-radio__original可以看到-->
            <input v-model="model" :name="name" :value="label" class="my-radio__original" type="radio" />
        </span>
        <span class="my-radio__label">
           <slot></slot>
            <!--如果没传内容,就用label的值-->
            <template v-if="!$slots.default">{{label}}</template>
        </span>
    </label>
</template>

<script>
    export default {
        name: "MyRadio",
        //=====新增inject========//
        inject:{
            radioGroup: {
                default:''
            }
        },
        computed:{
            //============需改造的部分============//
            model:{
                get(){
                    //如果有group包裹,就返回group上面的v-model值,否则就是radio上面的v-model
                    return this.isGroup ? this.radioGroup.value : this.value
                },
                set(val){

                    //this.$emit('input', val)
                    this.isGroup ? this.radioGroup.$emit('input', val)  : this.$emit('input', val)
                }
            },
            //=====新增isGroup========//

            isGroup(){
              // 判断radio是否有group包裹
              return !!this.radioGroup //强制转为布尔类型
            },
        },
        props:{
            label:{ //接收label
                type:[String,Number,Boolean],
                default:''
            },
            value:null, // 接收v-model
            name:{ // 有可能传name
                type:String,
                default:''
            }


        }
    }
</script>

<style lang="scss">
    .my-radio {
        color: #606266;
        font-weight: 500;
        line-height: 1;
        position: relative;
        cursor: pointer;
        display: inline-block;
        white-space: nowrap;
        outline: none;
        font-size: 14px;
        margin-right: 30px;
        -moz-user-select: none;
        -webkit-user-select: none;
        -ms-user-select: none;

        .my-radio__input {
            white-space: nowrap;
            cursor: pointer;
            outline: none;
            display: inline-block;
            line-height: 1;
            position: relative;
            vertical-align: middle;

            .my-radio__inner {
                border: 1px solid #dcdfe6;
                border-radius: 100%;
                width: 14px;
                height: 14px;
                background-color: #fff;
                position: relative;
                cursor: pointer;
                display: inline-block;
                box-sizing: border-box;
            }

            .my-radio__original {
                opacity: 0;
                outline: none;
                position: absolute;
                z-index: -1;
                top: 0;
                left: 0;
                right: 0;
                bottom: 0;
                margin: 0;
            }
        }

        .my-radio__label {
            font-size: 14px;
            padding-left: 10px;
        }
    }

    /*增加选中的样式*/
    .is-checked {
        .my-radio__input{
            .my-radio__inner{
                border-color:#409eff;
                background: #409eff;
                &:after{
                    transform: translate(-50%,-50%) scale(1);
                }
            }
        }
        .my-radio__label{
            color:#409eff;
        }
    }
</style>

测试一下。
App.vue:

<template>
  <div id="app">
<!--有group包裹-->
    <my-radio-group v-model="sex">
      <my-radio label="0">男</my-radio>
      <my-radio label="1">女</my-radio>
    </my-radio-group>
<!--无包裹-->
    <my-radio label="0" v-model="sex2">男</my-radio>
    <my-radio label="1" v-model="sex2">女</my-radio>
  </div>
</template>
<script>
  export default {
    data(){
      return {
        sex: '0',
        sex2: '1'
      }
    }
  }
</script>



相关文章

  • 10-3 form之radio组件

    单选组件 value选中的值,传递给radio-group中的bindchange。value可以改成自定义的值。...

  • 自定义radio-group组件

    如果radio只有少量的几个,我们完全可以每个都绑v-model,但如果radio太多的话,代码就显得啰嗦了,我们...

  • 自定义组件

    创建自定义组件 使用自定义组件

  • 微信小程序-自定义组件

    一、自定义组件介绍 微信小程序提供了自定义组件扩展机制,允许我们使用自定义组件的方式来构建页面。 自定义组件可以使...

  • vue 自定义组件(一)全局、局部组件

    vue自定义组件分为局部组件和全局组件 全局组件 全局组件格式template 是模板props 是自定义组件用到...

  • vue2.0组件间传值的方法汇总

    1、组件间的传值 1.1 父组件向子组件传值 子组件自定义一个属性 父组件通过自定义属性绑定值 子组件调用自定义属...

  • 调用自定义组件中的方法

    创建自定义组件 .json .js 页面中引入自定义组件 .json .xml 页面中调用自定义组件的方法 .js

  • 小程序的自定义组件

    小程序允许我们使用自定义组件的方式来构建页面。 自定义组件 1. 创建自定义组件 类似于页面,一个自定义组件由 j...

  • 微信小程序自定义组件传值问题

    微信小程序自定义组件传值问题 自定义组件 · 小程序 父组件(引用组件的页面模板)页面 子组件页面 表示父组件页面...

  • 小程序-自定义组件 ID属性笔记

    在自定义组件的时候,组件内包含了其他自定义组件:如课程列表组件,被状态组件包含(关于状态组件主要是指:加载中,没有...

网友评论

    本文标题:自定义radio-group组件

    本文链接:https://www.haomeiwen.com/subject/obdvektx.html