Implementing dynamic cols header field in layui

Demand determination

Recently, when making another demand, we need to dynamically update the header field in layui, so we have no choice but to move to the front-end camp
After a wave of official documents, I feel it's very simple and easy to understand
https://www.layui.com/doc/
Another online demonstration website
https://www.layui.com/demo/table/height.html

Before modification

The return value of the interface called by default in layui.render is a collection type, so the header and table data can be quickly generated according to the principle of one-to-one matching. However, if our header is generated dynamically, it will be troublesome. Here we need to involve the header data and table data.
So it is

layui.use('table', function() {
    var table = layui.table;

    table.render({
        elem: '#tableList'
        , url: '/web/aliyunOSSMetric/query?day=' + day + '&company=' + company
        , toolbar: true
        , height: 'full-330'
        , cols: [
            [
                 {field: 'bucket', width: 420, title: 'bucket' }
                ,{ title:'operation', toolbar: '#tableListAction', width:120}
                , {field: 'companyName', width: 150, title: 'manufacturer', sort: true}
                , {field: 'value', width: 200, title: 'flow(TB)', sort: true}
                , {field: 'percent', width: 200, title: 'proportion', sort: true}
            ]
        ]
    });
});

After modification

parseData method

In table, we can focus on an asynchronous callback function parseData, which can process the callback data, such as formatting the data. I originally intended to use this to render cols, but this is after the data is returned. Cols has taken effect before this method, so I can only find another way

done function

Similarly, the done function can do some things, but cols has been in effect before the function

Solution

Before the render method, first request the interface to obtain the data and process it into the desired format. You can directly assign a value to cols in render

layui.use('table', function() {
    let table = layui.table;
    let cols = []
    let col = []
    $.ajax({
        type: 'POST',
        url: '/web/aliyunOSSMetric/getCols?day=' + end + '&buckets=' + '&company=' + company,
        dataType: "JSON",
        sync: false,
        contentType: "application/json",
        success: function (res) {
            col.push({field: 'bucket', width: 420, title: 'bucket',merge: true});
            col.push({title:'operation', toolbar: '#tableListAction', width:120});
            col.push({field: 'companyName', width: 150, title: 'manufacturer', sort: true, rowspan: 1});
            $.each(res.data, function (index, obj) {
                col.push({field: obj, width: 200, title: obj, sort: true, rowspan: 1});
            });
            col.push({field: 'percent', width: 200, title: 'proportion', sort: true, rowspan: 1});
            cols.push(col);

            table.render({
                elem: '#tableList'
                ,
                url: '/web/aliyunOSSMetric/query?day=' + end + '&company=' + company
                ,
                toolbar: true
                ,
                height: 'full-330'
                ,
                cols: cols
            });
        }
    });
});

Then we will think that cols is not fixed, and the number of attributes of each element of the collection type returned by the back end is not fixed
For the return type of the back end, we can abstract it into an Object collection
For each Object, we can dynamically change the number of attributes of the business Object, then convert the business Object into a json Object, and then upward into an Object object Object
The details are as follows:

Create a tool class first

import com.google.common.collect.Maps;
import net.sf.cglib.beans.BeanGenerator;
import net.sf.cglib.beans.BeanMap;
import org.apache.commons.beanutils.PropertyUtilsBean;

import java.beans.PropertyDescriptor;
import java.util.Map;

/**
 * @author junfeng.lin
 * @date 2021/9/2
 */
public class ReflectUtil {
    public static Object getTarget(Object dest, Map<String, Object> addProperties) {
        PropertyUtilsBean propertyUtilsBean =new PropertyUtilsBean();
        PropertyDescriptor[] descriptors = propertyUtilsBean.getPropertyDescriptors(dest);
        Map<String, Class> propertyMap = Maps.newHashMap();
        for(PropertyDescriptor d : descriptors) {
            if(!"class".equalsIgnoreCase(d.getName())) {
                propertyMap.put(d.getName(), d.getPropertyType());
            }
        }
        // add extra properties
        addProperties.forEach((k, v) -> propertyMap.put(k, v.getClass()));
        // new dynamic bean
        DynamicBean dynamicBean =new DynamicBean(dest.getClass(), propertyMap);
        // add old value
        propertyMap.forEach((k, v) -> {
            try{
                // filter extra properties
                if(!addProperties.containsKey(k)) {
                    dynamicBean.setValue(k, propertyUtilsBean.getNestedProperty(dest, k));
                }
            }catch (Exception e) {
                e.printStackTrace();
            }
        });
        // add extra value
        addProperties.forEach((k, v) -> {
            try{
                dynamicBean.setValue(k, v);
            }catch (Exception e) {
                e.printStackTrace();
            }
        });
        Object target = dynamicBean.getTarget();
        return target;
    }

    public static class DynamicBean {
        /**
         * Target object
         */
        private Object target;

        /**
         * Attribute Collection
         */
        private BeanMap beanMap;

        public DynamicBean(Class superclass, Map<String, Class> propertyMap) {
            this.target = generateBean(superclass, propertyMap);
            this.beanMap = BeanMap.create(this.target);
        }


        /**
         * bean Add attributes and values
         *
         * @param property
         * @param value
         */
        public void setValue(String property, Object value) {
            beanMap.put(property, value);
        }

        /**
         * Get property value
         *
         * @param property
         * @return
         */
        public Object getValue(String property) {
            return beanMap.get(property);
        }

        /**
         * Get object
         *
         * @return
         */
        public Object getTarget() {
            return this.target;
        }


        /**
         * Generate objects based on attributes
         *
         * @param superclass
         * @param propertyMap
         * @return
         */
        private Object generateBean(Class superclass, Map<String, Class> propertyMap) {
            BeanGenerator generator =new BeanGenerator();
            if(null != superclass) {
                generator.setSuperclass(superclass);
            }
            BeanGenerator.addProperties(generator, propertyMap);
            return generator.create();
        }
    }
}

processing logic

service layer for processing

	Map<String,Object> properties = Maps.newHashMap();
	//Add attributes dynamically. The first parameter is the attribute name and the second parameter is the attribute value
   	properties.put(ossMetric.getName(), entity.getValue());
    ObjectMapper mapper = new ObjectMapper();
    //Convert to JSON object first
    String json = mapper.writeValueAsString(ReflectUtil.getTarget(vo,properties));	//Where vo is the specific business object
    //Then turn the JSON Object into an entity class. Because you don't know what the specific attribute value is, you can use Object to receive the upward transformation
    Object ossMetricVo = JSONObject.parseObject(json, Object.class);

Finally, you can directly return the Object array to the front end in the controller
Pseudo code in controller

@RequestMapping("/aliyunOSSMetric/query")
    public Map<String,Object> queryByDay(@RequestParam(name = "day") String day,
                                         @RequestParam(name = "company") String company
    ) throws InvocationTargetException, JsonProcessingException {
        Map<String,Object> res = new HashMap<>();
        List<Object> data = service.query(day, company);
        res.put("code", 0);
        res.put("msg", "");
        res.put("count", data .size());
        res.put("data", data);
        return res;
    }

In this way, the front end can automatically match the attribute name of the object in data according to the attribute name in cols. It's done!

Tags: html Layui

Posted on Fri, 03 Sep 2021 18:40:39 -0400 by trial_end_error