Play with the render function “component-dynamic-select” (top)

Custom slots

Select Slots provides prefix/ Empty built-in Slots, so our component that is closed based on el-Select needs to support user-defined slot content

Configuration items

Implement basic component functions based on the current configuration items

parameter instructions type The default value
slots slot Object {}
scopedSlots Scope slot Object {}

Render function

DynamicSelect/select.js

import SlotContent from './slotContent'
export default {
  / /... Ignore the configuration items defined in the previous article
  
  // Register a functional component to render the content provided to the component's outer slot
  components: { SlotContent },
  render(h) {
   // Config slot => used to render the component's built-in scope slot
    const slots = Object.keys(self.slots).map(slotName= > {
      return [h('slot-content', {
        props: {
          render: self.slots[slotName],
          data: self
        },
        slot: slotName,
        key: slotName
      })]
    })

  }
}
Copy the code

Now we need to modify the third parameter of createElement in the el-select rendering

The third argument to createElement is the contents of the current node, just like div -> can hold multiple spans, so the third argument includes:

  • The child node of the currently rendered element (component)
  • The built-in scope slot provided by the current render element (component) (identified by $scopeSlots)
  • The slot content slot of the currently rendered element (component)
return h('el-select', {
  class: self.className,
  staticClass: 'jl-full-line'. },Object.keys(this.$slots).map(s= > this.$slots[s]), ... optionsVnode, ... slots])Copy the code

With this we can render all three types of content into the el-Select component

DynamicSelect/select.js

export default {
  name: 'SlotContent'.// Declare this a functional component
  functional: true.props: {
    render: {
      type: Function.require: true
    },
    data: Object
  },
  render: (h, ctx) = > {
    return ctx.props.render(h, ctx.props.data)
  }
}
Copy the code

Flexible use of Render for highly customizable components

<template> <DynamicSelect ref="DynamicSelect" :value.sync="projectId" v-bind="projectSelectOption" :parse-data="parseData" :formatter="formatterValue" :multiple="true" collapse-tags // add customSlots configuration :slots="customSlots" > </DynamicSelect> </template> <script> export default { data() { return { customSlots: { prefix: this.prefixRender }, } }, methods: PrefixRender (h, vue) {// h => createElement // vue => DynamicSelect instance // We can use h to render any component into this custom slot. Return h('div', { staticClass: 'select-prefix', }, [h('svg-icon', { props: { 'icon-class': 'bug' }, })]) }, } } </script>Copy the code

The custom the options

Rendering πŸ’—

Continue to refine the render function

+ const { value, label, labelRender } = self.optionsProps
The + // option formats display
+ let labelRenderNode = null
+ if (labelRender && typeof labelRender === 'function') {
+ labelRenderNode = function(labelValue, op) {
+ return labelRender(h, labelValue, op)
+}
+}// Render 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. The only key value / / binding+ } labelRenderNode ? labelRenderNode(op[label], op) : null)]
  })
Copy the code

Formatter Customizes options content

<template> <DynamicSelect ... // add a customSlots configuration :slots="customSlots" :props="props" > </DynamicSelect> </template> <script> // define the const rendering options props = { label: 'proName', value: 'id', children: 'children', labelRender(h, label, item) {// h => createElement // label => options label // item => options Return [h('span', {staticClass: 'left', domProps: {innerText: label}}), h('span', {staticClass: 'Right ', domProps: {// On the right we show the first two bits of the project ID innerText: item?.id.substr(0, 2) || '' } })] } } export default { data() { return { customSlots: { prefix: This.prefixrender}, // Freeze the Object to avoid vue giving getters/setters to each member of the Object (also a performance optimization technique) : Object.freeze(props), } }, } </script>Copy the code

Add a full option to the component via $slots

<template> <DynamicSelect ... > <el-tag slot="selectAll" type="primary" class="select-tag-class" @click="handleSelectAll" > {{ selectAllLabel[selectAll] }} </el-tag> </DynamicSelect> </template> <script> export default { data() { return { SelectAllLabel: {false: 'selectAll ', true:' cancel ',}, // default: selectAll: false,}}, methods: HandleSelectAll () {this.selectAll =! This.selectall const list = [] if (this.selectAll) {// all} else {// cancel} // through $refs this.$refs.DynamicSelect.newValue = list } } } </script>Copy the code

$refs = $refs = $refs = $refs = $refs = $refs = $refs = $refs = $refs = $refs = $refs = $refs = $refs = $refs = $refs = $refs = $refs Tip -> (a [] is passed to the back end when our project selects all)

Why not pass in all project ids??

[A, B, C, D] [E, F] [E, F] [E, F] [E, F] [E, F] [E, F] [E, F] [E, F] [E, F] [E, F] [E, F] [E, F
Copy the code

Back to the previous question, how can I respond to dynamically pulled options in a timely manner? πŸ‘‰ Publish and subscribe

Release subscription

There’s a lot of ways to do it, but I’ll write it as a class

./DynamicSelect/Publish.js

// Publish subscribe mode (solve the problem of asynchronous pull data options)
export default class Publish {
  constructor() {
    this.pond = []
  }

  on(callBack) {
    this.pond.push(callBack)
  }

  emit(name) { 
    // Pass the name of the callback. If there is a match, the callback will be executed
    const fn = this.pond.find(c= > c.name === name)
    fn && fn()
  }
}
Copy the code

Add a custom event to the previous method

// Dynamically pull option data
async getOptionsData() {
	this.$emit('getOptionsSuccess'.this.optionsData)
}
Copy the code

Listen for this custom event

getOptionsSuccess(data) {
  // The outer layer stores a reference to options
	this.selectOptions = data
}
Copy the code

Perfect handleSelectAll

export default {
import Publish from './DynamicSelect/Publish.js'
const subscribe = new Publish()
  methods: {
    handleSelectAll() {
      // You can push callbacks to the event pool from within the method, or you can call the method push(depending on the execution timing)
      this.$nextTick(() = > {
        this.selectAll = !this.selectAll
        const list = []
        if (this.selectAll) { / / all
					list = this.selectOptions.map(i= > i.id)
        } else { / / cancel
					list = []
        }
        // Select all components ->
        // $refs references the component's newValue directly
        // Change value directly
        this.porjectId = list
      })
    }
  }
}
Copy the code

So far component encapsulation is complete, and the remote search piece is not written up, I found that writing this kind of article is not very easy to write, too much code and afraid of readers look confused, do not talk about the code is also difficult to explain. It is difficult to balance, which is why I have not updated my previous article “Developing mid-stage Projects with modularity in mind”. However, I have been learning this aspect, and there will be more in this series.

This component is wrapped in the Render function, not because it’s fancy. The Render function allows you to build components by writing JS, which is a lot more flexible than template syntax, but comes at a price

Many of the syntax sugar provided by Vue is unusable and needs to be implemented by itself, so you need to make a choice based on the functionality of the component

Component address πŸ‘‰github.com/it-beige/bl…

Write in the last

If there is that piece of writing is not good or there is a problem, you are welcome to point out, I will also keep modifying in the following article. I hope I can grow with you as I progress. Like my article friends can also pay attention to it

I will be grateful for the first attention. At this time, young you and I, travel light; Then, rich you and I, full of goods.

The articles

[Suggestion] To build the middle and background project with the idea of modularization

Chapter one: Developing middle and back office project with modularization idea

EventLoop is an EventLoop that can be used in an interview.

[Front-end system] Build a tall building from the foundation

[Front-end system] The application scenario of regularity in development is more than just rule verification

“Functional programming practical scenario | nuggets technical essay – double festival special article”

All the arcane points of CSS are here