preface

The research idea of this paper is to read Element source code, and then automatically write components step by step to improve their corresponding functions.

Believing that quantitative change causes qualitative change, we continue our study of Element’s components.

Previous studies:

Element component source research -Button

Element component source research -Input Input box

Element component source research -Layout, Link, Radio

It’s the same old approach: encapsulate the simplest components first and then add functionality to them.

The most basic implementation

Unlike a native Checkbox, which has a square checkbox, Element’s Checkbox has a content area.

<el-checkbox V-model ="checked"> Content area </el-checkbox>Copy the code

And whether it is selected is bound by v-model.

The basic implementation code is posted below:

<template>
    <label
        class="el-checkbox"
        :class="[
             { 'is-checked': isChecked }
        ]"
    >
        <span class="el-checkbox__input"
            :class="{
                'is-checked': isChecked,
            }"
        >
            <span class="el-checkbox__inner"></span>
            <input
                ref="checkbox"
                class="el-checkbox__original"
                type="checkbox"
                :checked="isChecked"
                @input="handleInput"
                @change="handleChange"
            >
        </span>
        <span class="el-checkbox__label" v-if="$slots.default">
            <slot></slot>
        </span>
    </label>
</template>

<script>
export default {
    name: 'ElCheckbox',
    props: {
        value: {},
    },
    computed: {
        isChecked() {
            return this.value
        }
    },
    methods: {
        handleChange(ev) {
            this.$emit('change', ev.target.checked, ev);
        },
        handleInput(ev) {
            this.$emit('input', ev.target.checked, ev);
        }
    },
}
</script>
Copy the code

I used to think of v-Model as a combination of :value and @input. Check (@input); check (@input); We can also bind the native checkbox with the V-model:

<! -- :checked="isChecked" @input="$emit('input', $event.target. Checked, ev)" --> <input ref="checkbox" class="el-checkbox__original" type="checkbox" V-model ="model" @change="handleChange" >... computed: { model: { get() { return this.value }, set(val) { this.$emit('input', val); } }, isChecked() { return this.model } },Copy the code

In this way, our implementation is close to the implementation of the source code.

Disabled state

Write the test code first

<el-checkbox V-model ="checked" disabled> Content area </el-checkbox>Copy the code

Add disabled to the component, depending on the style of the attribute, and add :disabled=”disabled” to the input tag.

The effect is as follows:

Checkboxes group

Test code:

<el-checkbox-group V-model ="checkList" @change="onChange"> <el-checkbox label=" Checkbox A"></el-checkbox> <el-checkbox Label =" Checkbox B"></el-checkbox> < el-Checkbox Label =" checkbox C"></ el-Checkbox >< el-Checkbox label=" disabled "disabled></ el-Checkbox > <el-checkbox label=" Select and disable "disabled></el-checkbox> </el-checkbox-group>Copy the code

A Checkbox group works differently from a single Checkbox. Instead of being true/false, it is an array bound to the group component, to which the selected Checkbox’s label is appended.

Write a CheckboxGroup component. The implementation idea is very similar to RadioGroup.

<template> <div class="el-checkbox-group" role="group"> <slot></slot> </div> </template> <script> export default { name:  'ElCheckboxGroup', componentName: 'ElCheckboxGroup', props: { value: {}, disabled: Boolean, }, }; </script>Copy the code

The CheckboxGroup component is just a container, and the main implementation logic is still in the Checkbox component.

Depending on how Checkbox groups work, we can also guess that the logic in the Checkbox component is implemented differently. The code looks like this:

model: { get() { return this.isGroup() ? this.store : this.value }, set(val) { if (this.isGroup()) { this.dispatch('ElCheckboxGroup', 'input', [val]); } else { this.$emit('input', val); }}},... isGroup() { let parent = this.$parent; while (parent) { if (parent.$options.componentName ! == 'ElCheckboxGroup') { parent = parent.$parent; } else { this._checkboxGroup = parent; return true; } } return false; }, store() { return this._checkboxGroup ? this._checkboxGroup.value : this.value; },Copy the code

The code above implements the operation on the parent Group’s :value value in the CheckBox.

The isGroup method is used to determine whether it is in the group. If so, a different set of logic is used. Val is not true/false. Rather, an array such as checkList:[‘ checkbox A’,’ selected and disabled ‘]. The value of the bound component has changed completely. Add value=”label” to the native component. When value has a value (label has a value, that is, in the group), the native component’s V-model will no longer bind checked attributes, but value attributes.

Check whether the current CheckBox is checked:

isChecked() { if ({}.toString.call(this.model) === '[object Boolean]') { return this.model; } else if (array.isarray (this.model)) {} else if (array.isarray (this.model)) { Return this.model.indexof (this.label) > -1; } return false; },Copy the code

The effect is as follows:

Indeterminate state

The indeterminate attribute is used to indicate the indeterminate state of the checkbox, and is generally used to achieve the effect of selecting all.

The test code is as follows:

<el-checkbox :indeterminate="isIndeterminate" V-model ="checkAll" @change="handleCheckAllChange"> All </el-checkbox> <div style="margin: 15px 0;" ></div> <el-checkbox-group v-model="checkedCities" @change="handleCheckedCitiesChange"> <el-checkbox v-for="city in Cities ":label="city" :key="city">{{city}}</el-checkbox> </el-checkbox> const cityOptions = [' Shanghai ', 'Beijing ',' Guangzhou ', 'shenzhen']; Data () {return {checkAll: false, checkedCities: [' Shanghai ', 'Beijing '], Cities: cityOptions, isIndeterminate: true}; }, methods: { handleCheckAllChange(val) { this.checkedCities = val ? cityOptions : []; this.isIndeterminate = false; }, handleCheckedCitiesChange(value) { let checkedCount = value.length; this.checkAll = checkedCount === this.cities.length; this.isIndeterminate = checkedCount > 0 && checkedCount < this.cities.length; }},Copy the code

Add the indeterminate attribute to the component, control the style, no complexity, effect is as follows:

Limits on the number of items available

  1. Component Add isLimitExceeded:false

  2. Add the isLimitDisabled calculation attribute. Determine whether a group can be selected based on its Max and min attributes.

isLimitDisabled() { const { max, min } = this._checkboxGroup; return !! (max || min) && (this.model.length >= max && ! this.isChecked) || (this.model.length <= min && this.isChecked); },Copy the code
  1. Finally, add the disablement logic for this.isLimitDisabled to the isDisabled calculation property.

The effect is as follows:

Button style

Create a new component called CheckboxButton. Detailed code can be found in the code cloud repository below.

With a frame

Add attributes to components to increase style determination. No further elaboration.

The effect is as follows:

conclusion

So much for the Checkbox component. Code in the code cloud: gitee.com/DaBuChen/my…

Other component source code study:

Element component source research -Button

Element component source research -Input Input box

Element component source research -Layout, Link, Radio

Element component source research -Checkbox multi-checkbox

Element component source research -InputNumber counter

Element component source code study -Loading component

Element component source research -Message component