preface

Hello, everyone! I am a DongXH,

Today, I would like to share a vue-tree-Selet component package with you. During the development process, we may encounter a new business requirement, or we may encounter an existing function to iterate on new functions.

The tree-select component is a modified iteration of the original business requirements, based on el-drawer nested El-tree and el-select drop-down selection.

It’s worth thinking about iterating on an existing feature.

Not just command+ C and command+ V!

This article summarizes some of the problems encountered in component encapsulation and the solutions, as well as my own idea of a component encapsulation.

Writing summary is not easy, if there is a better design and thought, look forward to collision, we make progress together.

Demand analysis

The general requirements are as follows:When I got the requirements draft, I looked at it firstelement-uitheel-treeA single component is unable to meet the requirements, so the need for secondary development of components, combinedel-selectandel-dialogImplement requirements.

Among them, several processes are carried out:

  • If all the children of the parent are checked, then by default only one ID of the parent is returned to the background. Otherwise, check all subsets in ourtreeThere will be 1W + nodes in the message, and all postbacks are extremely unfriendly to interface performance.
  • If all child nodes under the parent are unselected, all selected subsets are sent back.
  • Select the node, click to load subset data, reduce the component’s first rendering data.

Next, I thought about the functions to be implemented according to the requirements as follows:

  • Support one-time and lazy data loading.
  • Users can click to select by expanding categories. (PS: Click on the first panel to expand the first panel of its child nodes)
  • Initialization state is supported. The first parent node will be expanded by default or the output value is worth expanding.
  • Support for strict adherence to father-child disconnection.
  • Single and multiple options are supported.
  • Support select select box width custom, dialog width and height custom.

Component specific usage

After components are imported into the current page, the detailed Settings are as follows:

<select-tree
    v-model="Bound Model name"
    :width="100% ` `"
    :height="`auto`"
    :maxHeight="`400px`"
    :nodeKey="'department_id'"
    size=""
    multiple
    clearable
    :defaultProps="{ children: 'children', label: 'short_name' }"
    :defaultExpandedKeys="Default expanded array of nodes"
    :checkedKeys="Array of nodes selected by default"
    @change="changeTreeItem"
    :getGroupSequence="getGroupSequence"
/>
Copy the code

The specific implementation

The first step is to determine which fields the component needs to pass in from the parent.

  props: {
   // Get the tree-data interface
    getGroupSequence: {
      type: Function.default(){}},// props custom configuration
    defaultProps: { 
      type: Object.default() {
        return{}; }},// Whether the configuration can be multiple
    multiple: {
      type: Boolean.default() {
        return false; }},clear: {
      type: Boolean.default() {
        return false; }},// el-select configures whether the selection can be cleared
    clearable: {
      type: Boolean.default() {
        return false; }},// Whether to display the selected value as a text when configuring multiple selection
    collapseTags: {
      type: Boolean.default() {
        return false; }},// Set the nodeKey value of el-tree
    nodeKey: {
      type: String.default() {
        return "department_id"; }},// Displays the checkbox case whether to strictly follow the parent-child association
    checkStrictly: {
      type: Boolean.default() {
        return false; }},// The default selected node key array
    checkedKeys: {
      type: Array.default() {
        return[]; }},// The size style of el-select
    size: {
      type: String.default() {
        return "medium"; }},// The size style of el-select
    width: {
      type: String.default() {
        return `250px`; }},// The default height of the el-Dialog
    height: {
      type: String.default() {
        return `300px`; }},// A collection of tree nodes expanded by default
    defaultExpandedKeys: {
      type: Array.default() {
        return ["D1000001"]; }},// The default height of the el-Dialog. If the height is set to 100%, the maximum height will be set
    maxHeight: {
      type: String.default() {
        return `400px`; }},// Whether node data is loaded lazily
    lazy: {type: Boolean.default() {
        return true; }}},Copy the code

If the default is new and no defaultExpandedKeys are passed in, the default component renders a list of levels:

getTreeData(cb) {
  this.getGroupSequence({ department_id: "Level 1 Component ID" }).then(res= > {
    this.treeData = res.data;
    cb();
  });
},
Copy the code

In Mounted execute render data function:

mounted() {
    this.getTreeData(() = > {
      this.initCheckedData();
    });
},
Copy the code

There needs to be a callback function to perform the default height of el-Dialog before the callback function is executed:

initCheckedData() {
  if (this.multiple) {
    / / multi-select
    this.defaultExpand = this.defaultExpandedKeys;
    this.selectedData = this.checkedKeys;
    this.options = this.checkedKeys;
    this.checkedKeys.map(item= > {
      if (item) {
        this.defaultDeptAll.push(item.department_id); }}); }else {
    / / radio
    if (this.selectedData.length > 0) {
      this.checkSelectedNode(this.selectedData); }}this.$nextTick(() = > {
    this.popoverWidth = this.$refs.select.$el.clientWidth - 24;
  });
},
Copy the code

Load =”loadNode”;

loadNode(node, resolve) {
  if (node.level === 0) {
    return;
  }
  this.getGroupSequence({ department_id: node.data.department_id }).then(
    res= > {
      if (res.data && res.data.length > 0) {
        resolve(res.data);
        this.$refs.tree.setCheckedKeys(this.defaultDeptAll);
      } else{ resolve([]); }}); }Copy the code

Set to click the node according to the current parent node ID to obtain the subset, rendering. Then it is the judgment processing of the selection based on the single and multiple values passed in:

// Select the callback when the node is clicked, and return the data of the clicked node
handleNodeClick(data, node) {
  if (!this.multiple) {
    this.setSelectOption(node);
    this.isShowSelect = !this.isShowSelect;
    this.$emit("change".this.selectedData); }},// Multiple options, callback when the node status changes
handleCheckChange(data, checked, node) {
  console.log("handleCheckChange", data, checked, node);
  if (checked) {
    let array = this.$refs.tree.getNode(data.department_id);
    if (array.childNodes.length == 0) {
      this.checkedData.push(data);
      this.defaultDeptAll.push(data.department_id); }}else {
    if (this.checkedData.length > 0) {
      this.checkedData.forEach((item, index) = > {
        if (item.department_id == data.department_id) {
          this.defaultDeptAll.splice(index, 1);
          this.checkedData.splice(index, 1); }}); }}if (this.firstLoad || this.checkedKeys.length == 0) {
    this.setCheckedKey();
  } else {
    this.firstLoad = true;
  }
  this.$emit("change".this.defaultDeptAll, this.defaultExpand);
},
Copy the code

< span style = “box-sizing: inherit! Important; word-break: inherit! Important;”

// Delete the callback for any select option
removeSelectedNodes(val) {
  this.$refs.tree.setChecked(val, false);
  var node = this.$refs.tree.getNode(val);
  if (!this.checkStrictly && node.childNodes.length > 0) {
    this.treeToList(node).map(item= > {
      if (item.childNodes.length <= 0) {
        this.$refs.tree.setChecked(item, false); }}); }this.$emit("change".this.selectedData);
}
Copy the code

There are some other common functions of the function is not a list, the specific code please jump to see;

Component source code address

Git address – vue – tree – select

analyse

Fulfilling business requirements is not difficult, and there can be many ways to do it. I think it’s more about thinking through the development process and how you design that’s an important part of writing code at the beginning. The overall component design affects a lot, and a good design idea will make your code more reusable, extensible, and component performance.

There were a lot of problems during the development process, some of which stuck for a long time, such as re-rendering when a default value was reflected, reassigning the content exposed by a child component to a parent component caused the component to loop. Encounter problems, solve problems, reflect on the problem, to their own technical improvement has a great help ~

Currently, the component has been put into use, but there are still the following problems, which need to be further optimized in the later stage:

  • Our requirement is to return the parent ID if all subsets are checked, but if there is only one subset under the current parent, will the result return the current subset or the parent node?
  • el-treeThe current version has no search function.
  • Other unreasonable places for optimization iteration.

conclusion

In the process of development, I still think it is necessary to package components to design components, development ideas and development ability are equally important, clear thinking, get twice the result with half the effort.

This article is a recent project online, if you feel helpful, welcome to like, star~~

If you have a better idea or a way to do it, feel free to leave a comment in the comments section