Element UI Common Issues and Solutions

Element UI Common Issues and Solutions

This article documents practical solutions for common problems encountered when working with Element UI in Vue.js applications.

1. el-cascader Cascade Selector

a. Clear Value Programmatically

<el-cascader 
    ref="regionCascader" 
    :options="regionOptions" 
    v-model="selectedRegion"
    :show-all-levels="false" 
    expand-trigger="click"
    clearable
    change-on-select>
</el-cascader>

To clear the cascader value programmatically:

let eventProxy = {};
eventProxy.stopPropagation = () => {};
this.$refs.regionCascader.clearValue(eventProxy);

b. Enable Selection at Every Level

Add change-on-select atrtibute. Remove it to allow only leaf node selection.

c. Set Specific Value

let targetValue = [];
targetValue.push(someValue);
this.$refs.regionCascader.handlePick(targetValue);

d. Show/Hide Menu

this.$refs.regionCascader.showMenu();
this.$refs.regionCascader.hideMenu();

e. Searchable Cascader with Custom Input

<template>
    <el-cascader 
        filterable 
        ref="searchableCascader" 
        :options="locationData" 
        v-model="locationValue"
        :props="{ checkStrictly: true }"
        :show-all-levels="showLevels"
        :size="inputSize"
        :debounce="300" 
        style="width: 100%"
        :before-filter="beforeFilterLocation"
        @blur="handleBlur"
        @focus="handleFocus"
        @change="handleChange"
        @visible-change="handleVisibilityChange"
        :placeholder="placeholderText" 
        clearable
    ></el-cascader>
</template>

<script>
    import {cloneDeep} from '@/utils/objectUtils';
    import {fetchLocationData} from '@/api/locations';

    export default {
        name: "searchable-location-cascader",
        props: {
            modelValue: {},
            showLevels: {
                type: Boolean,
                default: false
            },
            inputSize: {
                type: String,
                default: 'medium'
            },
            placeholderText: {
                type: String,
                default: ''
            }
        },
        data() {
            return {
                locationList: [],
                locationData: [],
                locationDataBackup: [],
                locationDataLength: undefined,
                locationValue: [],
                userInputValue: undefined,
                incrementCounter: 1
            }
        },
        created() {
            this.loadLocationData();
        },
        mounted() {
            this.initializeValue();
        },
        methods: {
            loadLocationData() {
                fetchLocationData().then(response => {
                    this.locationList = response.data.attachData;
                    this.locationData = response.data.cascaderData;
                    this.locationDataLength = this.locationData.length;
                    this.locationDataBackup = cloneDeep(this.locationData);
                    this.initializeValue();
                })
            },
            initializeValue() {
                if (!this.modelValue) return;
                let value = [];
                let list = cloneDeep(this.locationDataBackup);
                let location = this.findLocationById(this.modelValue);
                if (!location) {
                    list.push(this.createItem(this.modelValue));
                    value.push(this.modelValue);
                } else {
                    if (location.treePosition) {
                        value = location.treePosition.substr(1).split('&');
                    }
                    value.push(this.modelValue);
                }
                this.locationData = list;
                this.locationValue = value;
            },
            findLocationById(id) {
                let location = undefined;
                if (this.locationList && id) {
                    for (let item of this.locationList) {
                        if (id === item.id) {
                            location = cloneDeep(item);
                            break;
                        }
                    }
                }
                return location;
            },
            createItem(name) {
                return {
                    value: name,
                    label: name,
                    children: undefined,
                    disabled: false
                }
            },
            beforeFilterLocation: function(inputValue) {
                this.userInputValue = inputValue;
                return false;
            },
            handleBlur() {
                if (!this.userInputValue) return;
                this.userInputValue = this.userInputValue.trim();
                if (this.userInputValue === "") return;
                this.$set(this.locationData, this.locationDataLength, this.createItem(this.userInputValue));
                this.locationValue = [this.userInputValue];
                this.userInputValue = undefined;
                this.handleChange(this.locationValue);
            },
            handleFocus() {
                this.$refs.searchableCascader.$refs.input.$refs.input.select();
            },
            handleChange(v) {
                if (v && v.length > 0) {
                    this.$emit('update:modelValue', v[v.length - 1]);
                } else {
                    this.$emit('update:modelValue', '');
                }
            },
            handleVisibilityChange(visible) {
                if (visible) return;
                if (this.locationValue && this.locationValue.length > 0) {
                    let locationId = this.locationValue[this.locationValue.length - 1];
                    let locationName = '';
                    let location = this.findLocationById(locationId);
                    if (location) {
                        locationName = location.name;
                    } else {
                        locationName = locationId;
                    }
                    this.$emit('locationChanged', locationId, locationName);
                } else {
                    this.$emit('locationChanged', '', '');
                }
            },
            setLocationValue(v) {
                this.locationValue = v;
            }
        }
    }
</script>

f. Get Current Label

<el-cascader ref="myCascader" :options="dataList" v-model="selectedData" @change="handleChange" size="small"></el-cascader>

fetchDataList(){
    this.dataList = [];
    fetchFromDatabase().then(response => {
        this.dataList = response.data;
        if(this.dataList.length > 0){
            let v = [];
            this.getFirstPath(v, this.dataList);
            this.selectedData = v;
            if (v.length > 0) {
                let selectedValue = v[v.length - 1];
                let selectedLabel = this.getCurrentLabels(v);
            }
        }
    })
},
getFirstPath(result, list) {
    if (list) {
        result.push(list[0].value);
        this.getFirstPath(result, list[0].children);
    }
},
getCurrentLabels(v) {
    let options = this.dataList;
    let labels = [];
    v.forEach(value => {
        const targetOption = options && options.filter(option => option['value'] === value)[0];
        if (targetOption) {
            labels.push(targetOption['label']);
            options = targetOption['children'];
        }
    });
    let label = this.$refs.myCascader.showAllLevels ? labels.join('/') : labels[labels.length - 1];
    return label;
},
handleChange(v){
    if (v.length > 0) {
        let selectedValue = v[v.length - 1];
        let selectedLabel = this.getCurrentLabels(v);
    }
}

2. Reset Form Fields or Clear Validation

validate - Validate entire form

this.$refs['myForm'].validate((valid, obj) => {});

validateField - Validate specific field

this.$refs['myForm'].validateField('fieldName', errorMsg => {});

resetFields - Reset all fields to initial values

this.$refs['myForm'].resetFields();

clearValidate - Remove validation results

this.$refs['myForm'].clearValidate();
this.$refs['myForm'].clearValidate(["field1","field2"]);

3. Form Validation

Complete Validation Example

// Reset form
this.$refs['myForm'].resetFields();

// Clear validation messages
this.$refs['myForm'].clearValidate();

// Validate entire form
this.$refs['myForm'].validate(valid => {
    if(valid) {
        // Validation passed
    } else {
        // Validation failed
    }
});

// Validate specific field
this.$refs['myForm'].validateField('username', errorMsg => {
    if (!errorMsg) {
        // Field is valid
    } else {
        // Field has error
    }
});

Conditional Validation

const validateChineseName = (rule, value, callback) => {
    if (/^[\u4e00-\u9fa5]{2,4}$/.test(value)) {
        callback();
    } else {
        callback(new Error('Please enter Chinese characters'));
    }
};        

validationRules: {
    username: [
        {required: true, message: 'Please enter username'},
        {validator: validateChineseName, trigger: ['blur','change']}
    ],
}

<el-form-item label="Username" prop="username" :rules="isEnabled ? validationRules.username : []">
    <el-input v-model="formData.username" maxlength="4"></el-input>
</el-form-item>

4. Force Component Re-render

this.$forceUpdate();

5. Set Cursor Focus

In Vue, DOM update are batched. Use nextTick to access updated DOM:

this.$nextTick(() => {
    this.$refs['inputField'].focus();
});

6. Custom Validation Rules

validationRules: {
    username: [
        {required: true, message: 'Please enter username'}
        {validator: validateUsername, trigger: ['blur','change']}
    ]
}
const validateUsername = (rule, value, callback) => {
    checkUsernameExists(value).then(response => {
        if (response.status == 200 && response.data) {
            callback(new Error(response.message));
            return;
        }
    });
    callback();
};

Check for non-numeric characters: /[^\d]/g.test(value) returns true if non-numeric characters exist.

7. Element Default Border Colors

Normal: border-color: #dcdfe6;

Focus: border-color: #409EFF;

Error: border-color: #f56c6c;

8. Custom Input Styles

input::-webkit-input-placeholder {
    color: #C0C4CC;
    font-size: 14px;
}
input:-moz-placeholder {
    color: #C0C4CC;
    font-size: 14px;
}
input:-ms-input-placeholder {
    color: #C0C4CC;
    font-size: 14px;
}

.customInput {
    margin-top: 2px;
    -webkit-appearance: none;
    background-color: #fff;
    background-image: none;
    border-radius: 4px;
    border: 1px solid #dcdfe6;
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
    color: #606266;
    display: inline-block;
    color: #303133;
    font-size: 14px;
    height: 40px;
    line-height: 40px;
    outline: 0;
    padding: 0 15px;
    -webkit-transition: border-color .2s cubic-bezier(.645,.045,.355,1);
    transition: border-color .2s cubic-bezier(.645,.045,.355,1);
    width: 160px;
}
.customInput input[type=text]:focus{
    outline: 1px solid #409EFF;
}

9. Form Array Element Validation

dataModel: {
    images: undefined,
    imageArray: []
}

imageArray structure: {name: undefined, url: undefined, del: false}

<el-form-item label="Images" prop="images">
    <el-input v-model="dataModel.images"></el-input>
    
    <div v-for="(item, index) in dataModel.imageArray" class="image-uploader">
        <el-form-item :prop="`imageArray[${index}].name`" :rules="{ required: true, message: 'Image name required', trigger: 'blur' }">
            <img :src="item.url" class="image-add">
            <el-input v-model="item.name" maxlength="20"></el-input>
        </el-form-item>
    </div>
</el-form-item>

10. Video Auto-fit el-col Width

video {
    width: 100%;
    height: 100%;
    object-fit: fill;
}

11. el-table Operations

Select Row

this.$refs.dataTable.toggleRowSelection(this.dataList[0]);

Column Content Overflow

el-table-column show-overflow-tooltip="true"
el-table-column tooltip-effect="light"

Lazy Loading

Set lazy property on el-table and use load function to load children for rows where hasChildren = true.

12. el-tree Operations

a. Set Selected Node Color

.el-tree--highlight-current .el-tree-node.is-current>.el-tree-node__content {
    background-color: #a6d1ff;
}

b. Select Tree Node

// By key
this.$refs.treeRef.setCurrentKey(nodeKey);

// By node object
this.$refs.treeRef.setCurrentNode(nodeObject);

// Select single node
this.$refs.treeRef.setChecked(nodeId, true, false);

// Clear selection
this.$refs.treeRef.setCheckedKeys([])

13. Chrome Browser Input Autofill Color

Change yellow background to white:

input:-webkit-autofill {
 box-shadow: 0 0 0px 1000px white inset !important;
}  
input:-webkit-autofill:focus {
 box-shadow: 0 0 0px 1000px white inset !important;
} 

Set transparent:

input:-internal-autofill-previewed,
input:-internal-autofill-selected {
    -webkit-text-fill-color: #FFFFFF !important;
    transition: background-color 5000s ease-in-out 0s !important;
}

14. Searchable Cascader Component

See section 1e for complete implementation example.

15. Expand Specific el-tree Node

this.$refs['treeRef'].store.currentNode.expanded = true
this.$refs['treeRef'].store.nodesMap["nodeId"].expanded = true

16. Date, Time, Datetime Pickers

<!-- Date -->
<el-date-picker v-model.trim="currentDate"
                value-format="yyyy-MM-dd"
                format="yyyy-MM-dd"
                type="date"
                placeholder="Select date"></el-date-picker>

<!-- Time -->
<el-time-picker v-model.trim="currentTime"
                value-format="HH:mm:ss"
                format="HH:mm:ss"
                placeholder="Select time"></el-time-picker>

<!-- Datetime -->
<el-date-picker v-model.trim="currentDateTime"
                value-format="yyyy-MM-dd HH:mm:ss"
                format="yyyy-MM-dd HH:mm:ss"
                type="datetime"
                placeholder="Select datetime"></el-date-picker>

value-format controls the value format, format controls the display format.

17. el-table-tree-column Issues

<el-table-tree-column 
    fixed
    :expand-all="false"
    :indent-size="20"
    child-key="children"
    levelKey="level"
    treeKey="value"
    parentKey="parentId"
    prop="label"
    label="Name"
>

Note: If treeKey is not specified, the object's ID property is used by default. Without levelKey, there will be no indentation between parent and child rows.

18. el-dialog Body Height Setting

Problem: Setting height: 100% on iframe doesn't expand el-dialog__body

Solution: Dynamicaly calculate and set height

data() {
    return {
        windowWidth: document.documentElement.clientWidth,
        windowHeight: document.documentElement.clientHeight,
        iframeHeight: '100%'
    };
},
watch:{
    'windowWidth':function(val){},
    'windowHeight':function(val){ 
        this.calculateIframeHeight();
    }
},
mounted() {
    var _this = this;
    window.onresize = function(){
        _this.windowWidth = document.documentElement.clientWidth;
        _this.windowHeight = document.documentElement.clientHeight;
    };
},
methods: {
    calculateIframeHeight() {
        this.$nextTick(()=>{
            this.iframeHeight = this.$refs['dialogRef'].$el.offsetHeight - 130 + 'px';
        })
    },
}

19. el-cascader TypeError Fix

When encountering TypeError: Cannot read property 'level' of null, add a key to force re-render:

<el-cascader 
    :key="cascaderKey"
    :options="cascaderOptions"
></el-cascader>

// In data
cascaderKey: 0,
cascaderOptions: []

// When updating data
++this.cascaderKey;
this.cascaderOptions = newOptions;

20. Password Field Auto-fill Issues

<el-input type="password" v-model.trim="passwordValue" maxlength="20" show-password auto-complete="new-password" placeholder="Password"></el-input>

Note: Use auto-complete="new-password" instead of autocomplete="off"

21. Table Single Selection

<el-table ref="dataTableRef" :data="tableData" stripe border highlight-current-row @selection-change="handleSelectionChange" @select="handleSelect">
</el-table>

handleSelect(selection, row) {
    this.selectedRows = [];
    let isChecked = false;
    for (let item of selection) {
        if (item.id === row.id) {
            isChecked = true;
            break;
        }
    }
    if (isChecked) {
        this.selectedRows.push(row);
    }
    
    if (this.tableData) {
        for (let rowItem of this.tableData) {
            if (rowItem.id === row.id) {
                this.$refs['dataTableRef'].toggleRowSelection(rowItem, isChecked);
            } else {
                this.$refs['dataTableRef'].toggleRowSelection(rowItem, false);
            }
        }
    }
},

handleSelectionChange(selection) {
    if (this.tableData) {
        for (let rowItem of this.tableData) {
            let isChecked = false;
            for (let selectedItem of this.selectedRows) {
                if (rowItem.id === selectedItem.id) {
                    isChecked = true;
                    break;
                }
            }
            this.$refs['dataTableRef'].toggleRowSelection(rowItem, isChecked);
        }
    }
},

// Clear selection
this.$refs['dataTableRef'].clearSelection();

Hide header checkbox:

<style scoped>
    /deep/.el-table__header-wrapper .el-checkbox {
        display:none
    }
</style>

22. el-checkbox Binding Value Issue

When changing binding value to false but checkbox appears checked:

<el-checkbox v-model="checkboxValue" @change="handleCheckboxChange(value, event)"></el-checkbox>

handleCheckboxChange(v, ev) {
  // Force uncheck
  this.checkboxValue = false;
  ev.target.checked = false;
}

23. Prevent el-dialog Close on Mask/ESC

<el-dialog v-model="dialogVisible" title="Confirmation" width="400" :close-on-click-modal="false" :close-on-press-escape="false">
    <div style="text-align: center;">{{message}}</div>
    <template #footer>
        <div class="dialog-footer">
            <el-button @click="confirmAction">Confirm</el-button>
            <el-button @click="cancelAction">Cancel</el-button>
        </div>
    </template>
</el-dialog>

24. Hide el-dialog Header

.no-header-dialog .el-dialog__header {
    display: none;
}

<el-dialog class="no-header-dialog" v-model="progressVisible" :show-close="false" :close-on-click-modal="false" :close-on-press-escape="false">
    <div style="text-align: right; width: 100%"><el-button type="danger" @click="closeDialog">Close</el-button></div>
    Content here
</el-dialog>

25. Style Overrides

::v-deep(.el-collapse-item__title) {
  width: 95%;
}

Tags: Vue.js element-ui frontend ui-components javascript

Posted on Mon, 13 Jul 2026 17:19:24 +0000 by mediabob