preface
This article, the second in the Vue review pose series, shares the implementation of the checkbox component.
Scaffolding please refer to the first: juejin.cn/post/701798… Oversnail.github. IO/over-snail-… Git address: github.com/overSnail/o…
Vue review series of UI components — buttons
introduce
Basic component that provides a set of options for the user to choose from, one option only.
The functionality to be implemented
attribute
function | instructions |
---|---|
v-model/value | Binding values |
disable | It is prohibited to use |
Radio button group | Provide a set of options to the user, and the V-model is bound to the parent level |
With a frame | Styles are enhanced and available in four sizes |
Button style | Styles are added to offer four sizes |
The event
The name of the event | instructions | The callback parameter |
---|---|---|
change | Trigger event when a binding value changes | The value value of the radio |
Function implementation
1. Basic functions
Create radio.vue and index.js in the SRC/Packages directory. Create a new radio.scss in the SRC /styles/index.scss directory and import it in SRC /styles/index.scss. We wrap input[type=radio] and span with the label tag. The goal is to spread the input mouse event across the label so that the input element can function properly even if it is hidden.
// radio.vue
<template>
<label class="my-radio" :class="{ ['my-radio-selected']: selected }">
<input class="my-radio-input" type="radio" @click="onClick" />
<span
class="my-radio-icon"
:class="{ ['my-radio-icon-selected']: selected }"
>
</span>
<span class="my-radio-label">
<slot></slot>
</span>
</label>
</template>
<script>
export default {
name: 'myRadio'.data() {
return {
selected: false.// Is selected}},props: {},
methods: {
onClick() {
this.selected = true
console.log('Click event trigger')}}},</script>
Copy the code
// radio.scss
@charset "UTF-8";
@import "common/var";
@import "mixins/mixins";
@include b(radio) {
display: inline-block;
box-sizing: border-box;
vertical-align: top;
font-size: $--font-size-large;
line-height: 20px;
height: 20px;
margin-right: 20px;
cursor: pointer;
&-input {
display: none;
}
&-icon {
box-sizing: border-box;
border: 1px solid #ddd;
height: 14px;
width: 14px;
border-radius: 50%;
background-color: #fff;
display: inline-block;
position: relative;
top: 2px;
&:after {
content: "";
position: absolute;
width: 4px;
height: 4px;
background-color: #fff;
left: 50%;
top: 50%;
border-radius: 50%;
transition: transform 0.2s;
transform: translate(-50%, -50%) scale(0);
}
&-selected {
background-color: $--color-primary;
&:after {
transform: translate(-50%, -50%) scale(1);
}
}
}
&-label {
display: inline-block;
margin-left: 3px;
}
&-selected {
color: $--color-primary; }}Copy the code
2. Basic usage
$emit(“input”); $bind:value; $emit(“input”); Considering that v-model may have a value during initialization, immediate must be set to true for watch, so that the radio can perform a value synchronization after initialization.
// radio.vue
<template>
<label class="my-radio" :class="{ ['my-radio-selected']: selected }">
<input class="my-radio-input" type="radio" @click="onClick" />
<span
class="my-radio-icon"
:class="{ ['my-radio-icon-selected']: selected }"
>
</span>
<span class="my-radio-label">
<slot></slot>
</span>
</label>
</template>
<script>
export default {
name: 'myRadio'.data() {
return {
selected: false.// Is selected}},props: {
value: {
type: [String.Number.Boolean].default: ""
},
label: {
type: [String.Number.Boolean].default: ""}},watch: {
// Initializes the check to see if the check is selected
value: {
handler(newVal) {
this.selected = this.value && this.value === this.label
},
immediate: true}},methods: {
onClick() {
this.selected = true
this.$emit("input".this.label); }},}</script>
Copy the code
3. In the disabled state
Use the disabled of native radio.
// button.vue
<template>
<label
class="my-radio"
:class="{ 'my-radio-selected': selected, 'my-radio-disabled': disabled }"
>
<input
class="my-radio-input"
type="radio"
@click="onClick"
:disabled="disabled"
/>
<span
class="my-radio-icon"
:class="{ 'my-radio-icon-selected': selected, 'my-radio-icon-disabled': disabled }"
>
</span>
<span class="my-radio-label">
<slot></slot>
</span>
</label>
</template>
<script>.props: {
// Disable status
disabled: {
type: Boolean.default: false,}}...</script>
Copy the code
4. The option group box is displayed
- This function is implemented by creating a
radio-group
Components willradio
Package.radio
Functions are taken over by the parent. - Will use the component communication
$dispatch
and$broadcast
, the former sends events up, and the latter broadcasts events down. - The VUE component lifecycle is
From the inside out
:Created -> child created -> child mounted -> parent mounted
, the parent component must be increated
Can not listen to events inmounted
In the listening. radio-group
thedisabled
Specific logic is relatively simple, just according todisabled
Value to adjust theradio
Components within themyDisabled
Properties. whileinput[type="radio"]
thedisabled
Properties bymyDisabled
anddisabled
Make joint decisions.
Create radio-group components: create a radio-group folder in the SRC/Packages directory. Create radio-group.vue and index.js in this folder. Create a new radio-group. SCSS in the SRC /styles/index.scss directory and import it in SRC /styles/index.scss.
// radio-group.vue
<template>
<div class="my-radio-group">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'myRadioGroup'.data() {
return {
options: []./ / options}},props: {
value: {
type: [Boolean.String.Number].default: false,},disabled: {
type: Boolean.default: false,}},watch: {
value: {
handler(newVal) {
this.syncValue(newVal)
},
immediate: true,},// Disable status
disabled: {
handler(newVal) {
this.syncOptionsDisable(newVal)
},
immediate: true,}},created() {
// Listen for the on-radio-add event and save the radio instance to options
this.$on('on-radio-add'.(radio) = > {
this.options.push(radio)
this.syncValue(this.value)
this.syncOptionsDisable(this.disabled);
})
// Listen for the on-radio-remove event to remove the radio instance from options
this.$on('on-radio-remove'.(radio) = > {
this.options.splice(this.options.indexOf(radio), 1)})// Listen for the radio select event and synchronize the value
this.$on('on-radio-select'.(radio) = > {
this.syncValue(radio.label)
})
},
methods: {
/ * * *@description Value Value synchronization *@param {Boolean/String/Number} FocusVal Selects the value, which is the Label property of the Radio component */
syncValue(focusVal) {
this.$emit('input', focusVal)
this.options.forEach((d) = > {
d.selected = d.label === focusVal
})
},
/ * * *@description Sets the myDisabled property * of the child option@param {Boolean} Disabled Specifies whether to disable */
syncOptionsDisable(disabled) {
this.options.forEach((d) = > {
d.myDisabled = disabled
})
},
},
}
</script>
Copy the code
Adjust the Radio component code to throw events to the radio-group.
// radio.vue omits some code. <template><label
class="my-radio"
:class="{ 'my-radio-selected': selected, 'my-radio-disabled': disabled || myDisabled }"
>
<input
class="my-radio-input"
type="radio"
@click="onClick"
:disabled="disabled || myDisabled"
/>
<span
class="my-radio-icon"
:class="{ 'my-radio-icon-selected': selected, 'my-radio-icon-disabled': disabled || myDisabled, }"
>
</span>
<span class="my-radio-label">
<slot></slot>
</span>
</label>
</template>
<script>.data() {
return {
selected: false.// Is selected
myDisabled: false.// Internal disabled attributes are controlled by the parent}},mounted() {
// Tell the myRadioGroup component to call the on-radio-add method with the current radio instance
this.dispatch('myRadioGroup'.'on-radio-add'.this)},beforeDestroy() {
// To remove, call the on-radio-remove method of the myRadioGroup component
this.dispatch('myRadioGroup'.'on-radio-remove'.this)},methods: {
onClick() {
this.selected = true
this.$emit('input'.this.label)
this.dispatch("myRadioGroup".'on-radio-select'.this)}},</script>.Copy the code
5. With borders
- to
radio
Components increaseborder
Property, you can render options with borders, this feature is mainly forcss
In the operation. border
To take effect,size
The same works. Need to develop 4 sizes, this function is also rightcss
In the operation.
// radio.vue omits some code
<template>
......
<label
class="my-radio"
:class="{ 'my-radio-selected': selected, 'my-radio-disabled': disabled || myDisabled, [`my-radio-${size}-border`]: border }">... </label> </template><script>
// the utility function is used to determine whether the value passed meets the criteria
import { oneOf } from '.. /.. /utils/assist';
export default {
props: {...// Whether to draw a border
border: {
type: Boolean.default: false,},/ / size
size: {
validator(value) {
return oneOf(value, ['large'.'medium'.'small'.'mini'])},type: String.default: 'medium',},... }}</script>
Copy the code
// radio.scss
@charset "UTF-8";
@import "common/var";
@import "mixins/mixins";
@include b(radio){...// size and border related styles
&-large-border {
height: 40px;
padding: 8px 8px 12px 8px;
border: 1px solid $--border-color;
border-radius: 4px;
}
&-medium-border {
height: 36px;
padding: 6px 8px 10px 8px;
border: 1px solid $--border-color;
border-radius: 4px;
}
&-small-border {
height: 32px;
padding: 4px 8px 8px 8px;
border: 1px solid $--border-color;
border-radius: 4px;
font-size: $--font-size-medium;
}
&-mini-border {
height: 28px; padding: 2px 8px 6px 8px; border: 1px solid $--border-color; border-radius: 4px; font-size: $--font-size-medium; }}Copy the code
6. Button styles
- Rendering radio to button style is also an operation on CSS.
- The button property is set to radio-group, and the parent takes over the function.
// radio.vue omits some code
<template>
<label
class="my-radio"
:class="{ 'my-radio-selected': selected, 'my-radio-disabled': disabled || myDisabled, [`my-radio-${size}-border`]: border, [`my-radio-${size}-button`]: button, 'my-radio-selected-button': selected && button, }"
>
<input
class="my-radio-input"
type="radio"
@click="onClick"
:disabled="disabled || myDisabled"
/>
<span
class="my-radio-icon"
:class="{ 'my-radio-icon-selected': selected, 'my-radio-icon-disabled': disabled || myDisabled, 'my-radio-icon-button': button }"
>
</span>
<span class="my-radio-label">
<slot></slot>
</span>
</label>
</template>
<script>.data() {
return {
button: false.// Button style, controlled by parent}}</script>
Copy the code
// radio-group.vue
<script>
......
props: {...// Whether to enable button style
button: {
type: Boolean.default: false}},watch: {...// Whether to use button style
button: {
handler(newVal) {
this.syncOptionsButtonStyle(newVal)
},
immediate: true,}},created() {
// Listen for the on-radio-add event and save the radio instance to options
this.$on('on-radio-add'.(radio) = >{...this.syncOptionsButtonStyle(this.button); })},methods: {
/ * * *@description Sets the button property of the child option to control the button style *@param {Boolean} Value Specifies whether to set */
syncOptionsButtonStyle(value) {
this.options.forEach((d) = > {
d.button = value
})
}
}
</script>
Copy the code
// radio.scss
@charset "UTF-8";
@import "common/var";
@import "mixins/mixins";
@include b(radio){...// Button style related styles
&-large-button {
float: left;
background-color: #fff;
height: 40px;
line-height: 38px;
padding: 0 15px 0 12px;
margin: 0;
border: 1px solid $--border-color;
}
&-medium-button {
float: left;
background-color: #fff;
height: 36px;
line-height: 34px;
padding: 0 15px 0 12px;
margin: 0;
border: 1px solid $--border-color;
}
&-small-button {
float: left;
background-color: #fff;
height: 32px;
line-height: 30px;
padding: 0 15px 0 12px;
margin: 0;
border: 1px solid $--border-color;
font-size: $--font-size-medium;
}
&-mini-button {
float: left;
background-color: #fff;
height: 28px;
line-height: 26px;
padding: 0 15px 0 12px;
margin: 0; border: 1px solid $--border-color; font-size: $--font-size-medium; }}Copy the code
conclusion
The above is part of the function development process of the single box component. In the single box group function, we created a new component radio-group as the parent, and used the broadcast and distribution mechanism in component communication to coordinate the mutual calls between the father and the son, so as to complete the realization of v-Model and Disabled functions.
Knowledge points involved
(Vue) : Class and style binding 2.(Vue) : V-model syntax sugar principle 3.(HTML) : Label label 4