Often used in the project to choose, choose color, choose brand… A variety of choices, as shown in the figure below, are usually made using Radio or checkbox, which usually requires a lot of logic and native style modification, which is time-consuming and costly. We simply use the most primitive way to package into components, only pay attention to the rendering of each Item, and put the logic such as selection into the components to complete. Just focus on event callbacks externally.
Again in terms of component requirements, we expect the following usage:
<CheckGroup class="selector" @onSelect="onSelect" deriction="x | y">
<CheckItem :renderItem="renderColorItem"/>.</CheckGroup>
onSelect:function(ids){
// List of project ids selected by the user
console.log(ids)
}
Copy the code
CheckGroup and CheckItem are used similarly to iView or Element forms. Yes, I borrowed the Form implementation. I put FromItem event handling (dependent collection, validation, etc.) on the top layer of the Form, injected data into the FormItem by providing, passed Form element events to the FormItem through eventBus, and finally reached From. In the Form layer to implement various functions, so as to achieve the effect of abstract logic, each component does its own job.
Architecture design
implementation
The CheckGroup implementation is simple and does the following things.
- 1 collect all items (my project is only collected to the parent container, not used, for future advanced functions).
- 2 Inject data into the top layer of child components by providing.
- 3 Handle events via eventBus, receiving child element clicks and other event data
First of all, in the Template layer, we usually put multiple choices into the scrollView (based on the battery-scroll), so we need to render different styles in different directions so that the outer scrollView can smoothly roll the battery-scroll document portal
<template>
<div class="multiCheck-container" :style="direction=='x' ? xStyle : yStyle">
<slot name="default"></slot>
</div>
</template>
Copy the code
export default {
props: {
multiCheck: {
type: Boolean.required: false.default: true
}
},
data () {
return {
itemsGroup: [].userSelect: {
id: [].item: []},// Item selected by the user
xStyle: {
// So that the inside of the container can be stretched horizontally
display: 'inline-block'.width: 'auto'.whiteSpace: 'nowrap'
},
yStyle: {
display: 'flex'.'flex-direction': 'row'.'flex-wrap': 'wrap'.width: '100%'}}},// Inject data into child components
provide () {
return {
choseData: this.userSelect,
chooseForm: this}},created: function () {
// Initializes the add item
this.$on('on-add-item', item => {
this.itemsGroup.push(item)
})
},
mounted: function () {
this.$on('on-click-item', item => {
if (this.multiCheck) {
/ / multi-select
const hasChecked = this.userSelect.id.some(v= > v === item.id)
if (hasChecked) {
// Double click to delete the item
let index = this.userSelect.id.findIndex(v= > v === item.id)
this.userSelect.id.splice(index, 1)
this.userSelect.item.splice(index, 1)}else {
// Add item with one click
this.userSelect.id.push(item.id)
this.userSelect.item.push(item)
}
} else {
/ / radio
this.userSelect.id = item.id
this.userSelect.item = item
}
// Notify externally selected projects via emit
this.$emit('onSelect'.this.userSelect)
})
}
}
Copy the code
Implement CheckItem
The things we need to do in CheckItem are
- 1 Add child elements to parent dependencies via eventBus
- 2 Notify the parent component that the child element is clicked, and pass the item information to the parent checkGroup component
checkItem.js
<template>
<div class="multicheckItem" :style="{display:FormParent.multiCheck? 'inline-block':'block'}">
<Render :render="renderItem" :bindData="{onItemClick,choseData,FormParent}" />
</div>
</template>
Copy the code
import Render from '.. /render'
export default {
components: { Render },
props: {
renderItem: {
type: Function.required: true}},inject: {
FormParent: {
from: 'chooseForm',},choseData: {
from: 'choseData',}},// Notify the parent component to collect child element instances
created: function () {
this.FormParent.$emit('on-add-item'.this)},methods: {
onItemClick: function (item) {
this.FormParent.$emit('on-click-item', item)
}
}
}
Copy the code
To make it easier for us to scale, we hand over the rendering of the child element to a functional component, so that when we call the checkout component, we just pass in a method called renderItem and return any element. He will carry onItemClick choseData, FormParent these three parameters in the past, we only need to apply colours to a drawing of the item in the renderItem method component processing click event as well as the style of the selected will be ok
Render.js
export default {
name: 'Render'.functional: true.props: {
render: [Function.String].bindData: [Object]},render: function (h, ctx) {
return ctx.props.render(h, ctx.props.bindData)
}
}
Copy the code
Take a chestnut
<CheckGroup class="selector" @onSelect="onSelect">
<CheckItem
v-for="(item,index) in ColorList"
:renderItem="(h,bindData)=>renderItem(h,bindData,item,index)"
/>
</CheckGroup>. Omit some codeconst ColorList =
[
{
id:1.color:'# 222'.name:'Light black'
},
{
id:1.color:'#F00'.name:'red'
},
{
id:1.color:'#0F0'.name:'green'
}
]
renderItem: function (h, bindData, item,index) {
return <ColorItem bindData={bindData} item={item} />
},
Copy the code
So in our ColorItem component:
ColorItem.js
<template>
<div class="brand_item" @click="onBrandClick">
<div
class="itemimg"// Add a background color to match the id, indicating that :style="{background:bindData.choseData.id.some(v=>item.id == v) ? '# 555' : ' '}"
></div>
<p>{{item.name}}</p>
</div>
</template>
<script>
export default {
props: {
item: {
type: Object,
required: true
},
bindData: {
type: Object
}
},
mounted: function () {},
methods: {
onBrandClick: function () {
this.bindData.onItemClick(this.item)
}
}
}
</script>
Copy the code