Let’s look at the effect first π
Two common uses of the SELECT component are shown above
- 1. Filtering of tables (filtering data according to items)
- 2. Use of forms (creating receipts based on projects)
Components use cases
Give a use case an overview of what functionality the component provides
- Supports formatting option data
- Custom event callback
listeners
- Insert the scope slot provided by the component
<template> <DynamicSelect type="select" v-model="value" v-bind="config"> </DynamicSelect> <div> {{value}}</div> </template> <script> import '@/components/FormSelect' export default { data() { return { value: ", config: {clearable: true, filterable: true, options: [{name: 'FFF1', id: '1', number: 'FFF1'}, {name: 'option 2, id:' 2 ', number: 'SSS2'}, {name: 'option 3, id:' 3 ', number: 'TTT3, disabled: true}, {name: four' 'option, id: 4, number: 'LLL4' } ], props: { label: 'name', value: 'id', formatter: (val, op) => { return `${val} ${op.number}` } }, multiple: true, listeners: { change: (val) => {console.log('change ===> ', val)}, dataChange: (val) => {console.log('dataChange ===> ', val)}, }, slots: { prefix: (h) => (<i class='el-icon-edit el-input__icon' />) } } } }, } </script>Copy the code
List filtering use
For lists that filter by item are used in many places, we can choose to define a mixin file to simplify the configuration of each page
DynamicSelectOptions.js
import { requestUrl } from '@/api/constant'
const DEFAULT_PAGE = 1
const DataTableMixin = {
data() {
return {
_url: ' '._params: {},
_query: {},
page: DEFAULT_PAGE,
offset: 20.loading: false.// Whether the data is being loaded
list: [].// Data list
total: 0._dataField: null.projectSelectOption: {
url: requestUrl.projectSettingsList,
props: {label: 'proName'.value: 'id'},
params: {status: 1.pageSize: 1000.pageIndex: 1},
searchKey: "projectName".clearable: true.filterable: true}}},methods: {
/** * Pull data *@returns {Promise<any>}* /
async dataTableInit() {
// Pull data according to the URL}}},export default DataTableMixin
Copy the code
A page
<template class="A app-container"> <FormSelect ref="FormSelect" value.sync="projectId" v-bind="projectSelectOption" :clearable="false" // If the clearance option is not allowed, you can override the configuration item that is bound to v-bind with the following command. false}) > </FormSelect> </template> <script type="text/ecmascript-6"> import '@/components/FormSelect' import DynamicSelectOptions form './DynamicSelectOptions' export default { data() { reutrn { projectId: '', } } } </script>Copy the code
Configuration items
Based on the currently provided configuration items to implement the basic functions of the component
parameter | instructions | type | The default value |
---|---|---|---|
value | data | [String .Boolean .Number .Array ] |
– |
formatter | Formatting data | Function |
– |
disabled | Whether to disable | Boolean |
false |
readonly | Whether the read-only | Boolean |
false |
options | Alternatives to the | Array |
– |
props | Alternative mapping | Object |
{label: 'label',value: 'value',children: 'children'} |
className | The name of the class | [String .Array ] |
– |
customStyle | Custom styles | Object |
– |
url | Get the data URL dynamically | String |
– |
method | Dynamic data acquisition request mode | String | GET |
params | Get data request parameters dynamically | Object |
– |
parseData | Parse the data returned by the interface | Function |
– |
searchable | Whether remote search is available | Boolean |
– |
searchKey | Search keyword field name | String |
label |
remoteMethod | Remote search method | Function |
– |
listeners | The event | Object |
{} |
Basic implementation
DynamicSelect/select.js
export default {
name: 'DynamicSelect'.props: {/* ζζΆε°±δΈι’ι£δΊ */},
inheritAttrs: false.inject: {
jlFormSubject: { default: null } // Accept the injected form instance
},
data() {
return {
// Default event
defaultOn: {
input: this.handleChange
},
optionsData: this.options,
loading: false.oldOptionsData: null,}},computed: {
// Because Vue cannot change the props value, Vue passes it with the new props
newValue: {
get({ value }) {
return value
},
set(val) {
// Bidirectional binding
this.$emit('update:value', val)
return val
}
},
// Support an incoming callback to the Listener object
onEvents() {
return Object.assign({}, this.defaultOn, this.listeners)
},
// props supports extensions, such as passing the formatter function to format
optionsProps() {
return Object.assign({}, defaultProps, this.props)
},
bindAttrs() {
const obj = {
// Placeholder is not displayed when disabled
placeholder: this.disabled ? ' ' : this.$attrs.placeholder || 'Please select'
}
// Add necessary props when remote search is enabled
if (this.searchable) {
obj.filterable = true
obj.remote = true
}
return Object.assign(
{},
this.$attrs,
obj
)
},
// Combine remote search params: increase fault tolerance as much as possible
requestOption() {
let paramsKey = this.method.toUpperCase() === 'GET' ? 'params' : 'data'
return {
baseUrl: this.$attrs.baseUrl,
url: this.url,
method: this.method
[paramsKey]: this.params || this.$attrs.data
}
}
},
watch: {
options: {
handler(value) {
this.optionsData = value
},
deep: true.immediate: true
},
// Allow dynamic modification of URL, params, method
requestOption: {
handler() {
if (this.url) {
this.getOptionsData()
}
},
deep: true}}},Copy the code
Which of the above are our dynamic data sources, from which we can generally get some tips for encapsulating components:
- The attribute is computed
getter/setter
Or$emit
To implement thev-model
- For example, for example, we don’t want to define the props that are critical for the user to pass, such as defining the props for requestOption for the user to pass the configuration items of the object.
- Instead of using props for all attributes that are too marginal, $attrs and props are logically integrated into a single attribute object with computed
Dynamic request data and response data changes
// This is the same file
created() {
// Uncascaded select
if (!Reflect.has(this.bindAttrs, 'cascade') && this.url) {
// getOptionsData: Return the promise after obtaining the data. You can do other operations after obtaining the data
this.getOptionsData().then(data= > {
// Select the first one automatically...})}},methods: {
// Pull the option data dynamically
async getOptionsData() {
return new Promise((resolve, reject) = > {
let {
requestOption,
parseData
} = this
this.loading = true
// Do the bottom check again
if(! requestOption.url)returnrequest(... requestOption).then((res = {}) = > {
this.loading = false
// The response data can be processed
{code: 200, pageData: {data: []}, pageIndex: 1, pageSize: 100,} => res.pageData? .data | | []} 2. For example: all we need to add a option: the return {data: res. PageData? .data ? [{label: 'all', id: 'all'},... res pageData. Data] : []} * /
if (parseData && typeof parseData === 'function') {
res = parseData(res)
}
if (Array.isArray(res.data)) {
this.optionsData = res.data
} else {
this.optionsData = []
}
resolve(this.optionsData)
}).catch((e) = > {
this.optionsData = []
this.loading = false
reject(e)
})
})
}
}
Copy the code
From the above, we can get a rough idea of how to encapsulate components:
- As much as possible
spread
(…). Operator, avoid writing dead keys, etc - Less controlled blocks give sovereignty to the user as much as possible, usually through callbacks
Render function
Let’s render the select using the render function, which is not written in JSX. If you do not know the suggestion to write to the document’s render, deep into the data object block. Portal π delve into data objects
render(h) {
const self = this
/ / rendering options
const optionsVnode = self.optionsData.map((op, index) = > {
return [h('el-option', {
attrs: {
value: op[value],
label: op[label],
disabled: op.disabled
},
key: op.id || op.value // Bind the unique key}]})return h('el-select', {
// Support custom className
class: self.className,
// Static className, which can be used to write styles
staticClass: 'jl-full-line'.Inline styles with high write permissions are also supported
style: self.customStyle,
// Pass all props and the native ATTRs support takes effect
attrs: {
...self.bindAttrs
},
props: {
value: self.newValue,
loading: self.loading, ... self.bindAttrs },on: {
...self.onEvents, // Select all listeners to listen to
input: self.handleInputEvent,
change: self.handleChangeEvent,
'visible-change': self.handleVisibleChange
},
}, optionsVnode)
}
Copy the code
From the above, we can get a rough idea of how to encapsulate components:
spread
(…). Operator to make your component properties massively extensible
At this point, one might ask, why not use $listener instead of having to implement your own custom event via callback?
If you’ve written a lot of basic components you probably have
When a component is used as a child of another component, the other component will also pass down custom events that it listens for via v-on=$listener. If a component is nested too many times, it is possible that events inside the component will be affected by external components. Events such as input and change are heard by your component itself, but are also heard by the outer layer through passthrough
We won’t parse the input and change events that we listened for, but let’s talk a little bit about handleValueChanged
Data: [{id: '1', name: 'A'}, {id: '2', name: 'A'} [{id: '1', name: 'A'}, {id: '2', name: 'B'}] 'A', projectEntrys: {id: '1', name: 'A'}, ... }] */
Copy the code
We can’t listen to events to change the value. Formatter is the best way to do this. Write logic according to the current business
The following content will be updated in the next chapter
- Customize slots to render component content
- Provides the component’s outer render function to flexibly render the scope slots provided by the component
Rendering π
Write in the last
If there is a piece of writing in the article is not very good or there are questions welcome to point out, I will also keep modifying in the following article. I hope I can grow with you as I progress. Those who like my article can also pay attention to it
I’ll be grateful to the first people to pay attention. At this time, you and I, young, packed lightly; And then, rich you and I, full of it.
The articles
The idea of modularity is to build the middle and background projects
The first chapter is to develop the background project with modularity
γ Front-end system γ Talk about understanding EventLoop from an interview question (updated analysis of four advanced questions)
[Front end system] Build a great oaks from the foundation
[Front-end System] The application scenario of re in development is not just rule verification
“Functional programming practical scenario | nuggets technical essay – double festival special article”
γ suggested favorites γ CSS obscure points are all here