Add a low cost column hide controller to your table page

Recently, I received a demand, the background is the user feedback that there are generally too many columns in the table. Different user groups focus on different columns, and they expect to customize and control which columns are displayed.

This requirement is not complicated and can be easily implemented with V-IF control. The scary thing is that there are hundreds of such pages, and each page may contain 20-40 different columns. It would be too elegant to control each column with a V-IF statement.

implementation

Core ideas

Because of the large amount of code, the best way to achieve the lowest cost implementation is to register a component with the same name in the entry file to override the original table component without modifying the original tags and attributes. The following code uses the Element library example

import CtrlTable from '@/components/CtrlTable'
Vue.component('el-table', CtrlTable)
Copy the code

Next to write CtrlTable this component, our core appeal is to modify the stock of code as little as possible, so here to first attR and event monitor pass down

CtrlTable.vue

import { Table } from 'element-ui'

export default {
  name: 'CtrlTable'.components: {
    'origin-table': Table
  },
  render (h) {
    return h('origin-table', {
      attrs: this.$attrs,
      ref: 'table'.on: this.$listeners
    }, this.$slots.default)
  }
}
Copy the code

The above component imports the Table component from the Element-UI and returns it directly in the render function. This component is meant to be an intermediate component that accepts attrs and event listeners already written in the el-table tag in the old code. They are then passed to the real El-Table component. At this point, you can look at the pages that contain the El-Table in the page, and they appear in the browser normally. They look exactly the same as before, but in fact there is an extra layer in the middle.

Completion implementation

Add the control statement, which is actually quite simple. Just filter $slots.default and receive checked props for which column to display

CtrlTable.vue

import { Table } from 'element-ui'

export default {
  name: 'CtrlTable'.components: {
    'origin-table': Table
  },
  props: {
    checked: {
      type: [Array.undefined].default: undefined}},computed: {
    columns () {
      if (!this.checked) {
        return this.$slots.default
      }
      return this.$slots.default.filter(item= > (
        this.checked.includes(item.componentOptions && item.componentOptions.propsData.label) || ! (item.componentOptions && item.componentOptions.propsData.label) || (item.componentOptions && item.componentOptions.propsData.label ==='operation')
      ))
    }
  },
  render (h) {
    return h('origin-table', {
      attrs: this.$attrs,
      ref: 'table'.on: this.$listeners
    }, this.columns)
  }
}
Copy the code

Note that one little detail is that if you don’t have checked, it will render all of the $slots.default. The nice thing about this is that if you have pages that don’t need column control, you can still render all of the columns as long as you don’t pass checked.

Next is the implementation of the controller, drawn into a mixin, the logic is relatively simple, directly paste code

columnsCtrl.js

import Setting from '@/components/Setting'
const CONDITION_STORAGE_KEY = 'localstorage_key_demo'

export default {
  components: {
    Setting
  },

  data: () = > ({
    tableCondition: [].tableChecked: []
  }),

  mounted () {
    this.__initCondition()
  },

  methods: {
    __initCondition () {
      const {
        table
      } = this.$refs
      if (table && table.$slots && table.$slots.default.length) {
        this.tableCondition = table.$slots.default.map(item= > item.componentOptions && item.componentOptions.propsData.label).filter(item= >!!!!! item && item ! = ='operation')}this.__checkView(this.__getCache('table'))
    },

    __checkView (checked) {
      const { table } = this.$refs
      this.tableChecked = checked || table.$slots.default.map(item= > item.componentOptions && item.componentOptions.propsData.label).filter(item= >!!!!! item && item ! = ='operation')
    },

    __getCache () {
      const { name } = this.$route
      const cacheKey = `${name}`
      const cache = JSON.parse(localStorage.getItem(CONDITION_STORAGE_KEY))
      if (cache && cache[cacheKey]) {
        return cache[cacheKey]
      }
      return null
    },

    handleSettingShow (cb) {
      const cache = this.__getCache()
      cb && cb(cache || [...this.tableCondition])
    },

    handleSettingSave ({ cb, checked }) {
      const { name } = this.$route
      const cacheKey = name
      let cache = JSON.parse(localStorage.getItem(CONDITION_STORAGE_KEY))
      if(! cache) { cache = {} } cache[cacheKey] = [...checked]localStorage.setItem(CONDITION_STORAGE_KEY, JSON.stringify(cache))
      this.__checkView(checked)
      cb && cb()
    }
  }
}
Copy the code

The controller component part of the code is relatively simple, omitted here

demo

The complete code

Copy the code