美文网首页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>
    
    
    
    

    相关文章

      网友评论

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

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