QueryBuilder - HTML UI Elements for Mobile & Web Applications | www.HtmlElements.com

Overview

Smart.QueryBuilder represents a control for building filter queries. Queries with multiple groups of conditions and different operators can be created.

Files

  • CSS files
    • smart.default.css - the CSS file containing the styles for the element.
  • Javascript module
    • smart.querybuilder.js - the JS module which is in the source/modules/ folder and loads all script dependencies.
  • Javascript files
    • smart.element.js - the base class.
    • smart.filterbuilder.js - the JS file containing the definition for the element.

Usage

  • Import a module

    With this approach, we import a module and create the web component by using the Smart function. The #queryBuilder is a smart-query-builder tag.

    import { smartQueryBuilder } from "../../../source/modules/smart.querybuilder.js";
    
    Smart('#queryBuilder', class {
        get properties() {
            return {
                allowDrag: true,
                fields: [
                    { label: 'Id', dataField: 'id', dataType: 'number' },
                    { label: 'Product', dataField: 'productName', dataType: 'string' },
                    { label: 'Unit Price', dataField: 'price', dataType: 'number' },
                    { label: 'Purchased', dataField: 'purchased', dataType: 'datetime' },
                    { label: 'Available', dataField: 'available', dataType: 'boolean' }
                ]
            }
        }
    });
    
    document.readyState === 'complete' ? init() : window.onload = init;
    
    function init() {
    
    }
    
    Using the Smart function is optional. You can use const queryBuilder = document.querySelector("#queryBuilder"); and set the properties like that:
    const queryBuilder = document.querySelector("#queryBuilder");
    
    queryBuilder.allowDrag = true;
    queryBuilder.fields = [
        { label: 'Id', dataField: 'id', dataType: 'number' },
        { label: 'Product', dataField: 'productName', dataType: 'string' },
        { label: 'Unit Price', dataField: 'price', dataType: 'number' },
        { label: 'Purchased', dataField: 'purchased', dataType: 'datetime' },
        { label: 'Available', dataField: 'available', dataType: 'boolean' }
    ];
    
  • Import a module, Init on Demand

    The following imports the web component's module and creates it on demand, when the document is ready. The #queryBuilder is a DIV tag.

    import { smartQueryBuilder } from "../../../source/modules/smart.querybuilder.js";
    
    document.readyState === 'complete' ? init() : window.onload = init;
    
    function init() {
        const queryBuilder = new smartQueryBuilder('#queryBuilder', {
            allowDrag: true,
            fields: [
                { label: 'Id', dataField: 'id', dataType: 'number' },
                { label: 'Product', dataField: 'productName', dataType: 'string' },
                { label: 'Unit Price', dataField: 'price', dataType: 'number' },
                { label: 'Purchased', dataField: 'purchased', dataType: 'datetime' },
                { label: 'Available', dataField: 'available', dataType: 'boolean' }
            ]
        });
    }
    
  • Load scripts

    The following code adds the custom element to the page.

    <!DOCTYPE html>
    <html>
    <head>
        <link rel="stylesheet" href="./../../source/styles/smart.default.css" type="text/css" />
        <script type="text/javascript" src="../../source/smart.element.js"></script>
        <script type="text/javascript" src="../../source/smart.button.js"></script>
        <script type="text/javascript" src="../../source/smart.scrollbar.js"></script>
        <script type="text/javascript" src="../../source/smart.menu.js"></script>
        <script type="text/javascript" src="../../source/smart.input.js"></script>
        <script type="text/javascript" src="../../source/smart.complex.js"></script>
        <script type="text/javascript" src="../../source/smart.math.js"></script>
        <script type="text/javascript" src="../../source/smart.numeric.js"></script>
        <script type="text/javascript" src="../../source/smart.numerictextbox.js"></script>
        <script type="text/javascript" src="../../source/smart.calendar.js"></script>
        <script type="text/javascript" src="../../source/smart.date.js"></script>
        <script type="text/javascript" src="../../source/smart.draw.js"></script>
        <script type="text/javascript" src="../../source/smart.dropdownlist.js"></script>
        <script type="text/javascript" src="../../source/smart.listbox.js"></script>
        <script type="text/javascript" src="../../source/smart.timepicker.js"></script>
        <script type="text/javascript" src="../../source/smart.tooltip.js"></script>
        <script type="text/javascript" src="../../source/smart.datetimepicker.js"></script>
        <script type="text/javascript" src="../../source/smart.checkbox.js"></script>
        <script type="text/javascript" src="../../source/smart.filterbuilder.js"></script>
    </head>
    <body>
        <smart-query-builder></smart-query-builder>
    </body>
    </html>
    

    Note how smart.element.js is declared before everything else. This is mandatory for all custom elements.

Create, Append, Remove, Get/Set Property, Invoke Method, Bind to Event


Create a new element:
const queryBuilder = document.createElement('smart-query-builder');

Append it to the DOM:
document.body.appendChild(queryBuilder);

Remove it from the DOM:
queryBuilder.parentNode.removeChild(queryBuilder);

Set a property:
queryBuilder.propertyName = propertyValue;

Get a property value:
const propertyValue = queryBuilder.propertyName;

Invoke a method:
queryBuilder.methodName(argument1, argument2);

Add Event Listener:
const eventHandler = (event) => {
// your code here.
};

queryBuilder.addEventListener(eventName, eventHandler);

Remove Event Listener:
queryBuilder.removeEventListener(eventName, eventHandler, true);

Building a Filter Query

A complex filter query can be built by adding and manipulating conditions in the QueryBuilder's UI. In the element's initialization code, an array of fields to choose from is passed.

<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" type="text/css" href="./../../source/styles/smart.default.css" />
    <script type="text/javascript" src="../../source/smart.element.js"></script>
    <script type="text/javascript" src="../../source/smart.button.js"></script>
    <script type="text/javascript" src="../../source/smart.scrollbar.js"></script>
    <script type="text/javascript" src="../../source/smart.menu.js"></script>
    <script type="text/javascript" src="../../source/smart.input.js"></script>
    <script type="text/javascript" src="../../source/smart.complex.js"></script>
    <script type="text/javascript" src="../../source/smart.math.js"></script>
    <script type="text/javascript" src="../../source/smart.numeric.js"></script>
    <script type="text/javascript" src="../../source/smart.numerictextbox.js"></script>
    <script type="text/javascript" src="../../source/smart.calendar.js"></script>
    <script type="text/javascript" src="../../source/smart.date.js"></script>
    <script type="text/javascript" src="../../source/smart.draw.js"></script>
    <script type="text/javascript" src="../../source/smart.dropdownlist.js"></script>
    <script type="text/javascript" src="../../source/smart.listbox.js"></script>
    <script type="text/javascript" src="../../source/smart.timepicker.js"></script>
    <script type="text/javascript" src="../../source/smart.tooltip.js"></script>
    <script type="text/javascript" src="../../source/smart.datetimepicker.js"></script>
    <script type="text/javascript" src="../../source/smart.checkbox.js"></script>
    <script type="text/javascript" src="../../source/smart.filterbuilder.js"></script>
    <script>
        Smart('#queryBuilder', class {
            get properties() {
                return {
                    allowDrag: true,
                    fields: [
                        { label: 'Id', dataField: 'id', dataType: 'number' },
                        { label: 'Product', dataField: 'productName', dataType: 'string' },
                        { label: 'Unit Price', dataField: 'price', dataType: 'number' },
                        { label: 'Purchased', dataField: 'purchased', dataType: 'datetime' },
                        { label: 'Available', dataField: 'available', dataType: 'boolean' }
                    ]
                };
            }
        });
    </script>
</head>
<body>
    <smart-query-builder id="queryBuilder"></smart-query-builder>
</body>
</html>

Demo

The shown operations result in the following value:

[[["price","<","300"]],"and",[["available","=",true],"or",["productName","startswith","app"]]]

Whether or not conditions can be dragged is controlled by the property allowDrag.

Setting value Initially

A QueryBuilder can be initialized with a predefined value, as shown in the example below:

<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" type="text/css" href="./../../source/styles/smart.default.css" />
    <script type="text/javascript" src="../../source/smart.element.js"></script>
    <script type="text/javascript" src="../../source/smart.button.js"></script>
    <script type="text/javascript" src="../../source/smart.scrollbar.js"></script>
    <script type="text/javascript" src="../../source/smart.menu.js"></script>
    <script type="text/javascript" src="../../source/smart.input.js"></script>
    <script type="text/javascript" src="../../source/smart.complex.js"></script>
    <script type="text/javascript" src="../../source/smart.math.js"></script>
    <script type="text/javascript" src="../../source/smart.numeric.js"></script>
    <script type="text/javascript" src="../../source/smart.numerictextbox.js"></script>
    <script type="text/javascript" src="../../source/smart.calendar.js"></script>
    <script type="text/javascript" src="../../source/smart.date.js"></script>
    <script type="text/javascript" src="../../source/smart.draw.js"></script>
    <script type="text/javascript" src="../../source/smart.dropdownlist.js"></script>
    <script type="text/javascript" src="../../source/smart.listbox.js"></script>
    <script type="text/javascript" src="../../source/smart.timepicker.js"></script>
    <script type="text/javascript" src="../../source/smart.tooltip.js"></script>
    <script type="text/javascript" src="../../source/smart.datetimepicker.js"></script>
    <script type="text/javascript" src="../../source/smart.checkbox.js"></script>
    <script type="text/javascript" src="../../source/smart.filterbuilder.js"></script>
    <script>
        Smart('#queryBuilder', class {
            get properties() {
                return {
                    allowDrag: true,
                    fields: [
                        { label: 'Id', dataField: 'id', dataType: 'number' },
                        { label: 'Product', dataField: 'productName', dataType: 'string' },
                        { label: 'Unit Price', dataField: 'price', dataType: 'number' },
                        { label: 'Purchased', dataField: 'purchased', dataType: 'datetime' },
                        { label: 'Available', dataField: 'available', dataType: 'boolean' }
                    ],
                    value: [
                        [
                            ['purchased', '=', new Date(2019, 0, 4)],
                            'and',
                            ['productName', '<>', 'Monitors'],
                            'or',
                            ['productName', 'isblank']
                        ],
                        'and',
                        [
                            ['available', '=', true],
                            'and',
                            ['price', '<', 1300],
                        ]
                    ]
                };
            }
        });
    </script>
</head>
<body>
    <smart-query-builder id="queryBuilder"></smart-query-builder>
</body>
</html>

Demo

Custom Operations

Apart from standard comparison operations, such as Equals, Greater than, Less than, and Contains, users can define their own custom operations to use when creating queries with Smart.QueryBuilder:

Custom operations are defined by setting the customOperations property. Each member of this array can have the following fields:

  • label - label to be displayed in the operator box. Multiple operations with the same label can exist.
  • name - unique name of the operation
  • editorTemplate - callback function that creates a custom value editor
  • valueTemplate - callback function that displays the value after the edior has been closed
  • handleValue - callback function that handles the value returned by the editor when it is closed
  • hideValue - a boolean condition that specifies whether the operation requires a value or not

The name of the custom operation also has to be included in the filterOperations property/array of the field(s) it is applicable to.

<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" type="text/css" href="./../../source/styles/smart.default.css" />
    <script type="text/javascript" src="../../source/smart.element.js"></script>
    <script type="text/javascript" src="../../source/smart.button.js"></script>
    <script type="text/javascript" src="../../source/smart.scrollbar.js"></script>
    <script type="text/javascript" src="../../source/smart.menu.js"></script>
    <script type="text/javascript" src="../../source/smart.input.js"></script>
    <script type="text/javascript" src="../../source/smart.complex.js"></script>
    <script type="text/javascript" src="../../source/smart.math.js"></script>
    <script type="text/javascript" src="../../source/smart.numeric.js"></script>
    <script type="text/javascript" src="../../source/smart.numerictextbox.js"></script>
    <script type="text/javascript" src="../../source/smart.calendar.js"></script>
    <script type="text/javascript" src="../../source/smart.date.js"></script>
    <script type="text/javascript" src="../../source/smart.draw.js"></script>
    <script type="text/javascript" src="../../source/smart.dropdownlist.js"></script>
    <script type="text/javascript" src="../../source/smart.listbox.js"></script>
    <script type="text/javascript" src="../../source/smart.timepicker.js"></script>
    <script type="text/javascript" src="../../source/smart.tooltip.js"></script>
    <script type="text/javascript" src="../../source/smart.datetimepicker.js"></script>
    <script type="text/javascript" src="../../source/smart.checkbox.js"></script>
    <script type="text/javascript" src="../../source/smart.filterbuilder.js"></script>
    <script>
        Smart('#queryBuilder', class {
            get properties() {
                return {
                    customOperations: [
                        { label: 'Matches /^\d{7}$/g', name: '/^\d{7}$/g', hideValue: true },
                        {
                            label: 'Is valid',
                            name: 'isvalid',
                            editorTemplate: function (fieldType, value, fieldData) {
                                const editor1 = document.createElement('smart-radio-button'),
                                    editor2 = document.createElement('smart-radio-button'),
                                    container = document.createElement('div');

                                editor1.innerHTML = 'Yes';
                                editor2.innerHTML = 'No';
                                container.className = 'container';

                                if (typeof value !== 'boolean') {
                                    value = !!parseFloat(value);
                                }

                                editor1.checked = value;
                                editor2.checked = !value;

                                container.appendChild(editor1);
                                container.appendChild(editor2);

                                return container;
                            },
                            valueTemplate: function (editor, value) {
                                return value ? '<em>yes</em>' : '<em>no</em>';
                            },
                            handleValue: function (editor) {
                                const editors = editor.getElementsByTagName('smart-radio-button');

                                return editors[0].checked;
                            }
                        }],
                    fields: [
                        { label: 'Id', dataField: 'id', dataType: 'number', filterOperations: ['=', '<', '>', 'isvalid'] },
                        { label: 'Product', dataField: 'productName', dataType: 'string' },
                        { label: 'Product code', dataField: 'productCode', dataType: 'string', filterOperations: ['=', '/^\d{7}$/g'] },
                        { label: 'Unit Price', dataField: 'price', dataType: 'number' },
                        { label: 'Produced', dataField: 'produced', dataType: 'date', filterOperations: ['<', '>'] },
                        { label: 'Purchased', dataField: 'purchased', dataType: 'datetime' },
                        { label: 'Available', dataField: 'available', dataType: 'boolean' }
                    ],
                    value: [
                        [
                            ['productCode', '/^\d{7}$/g'],
                            'or',
                            ['id', 'isvalid', true]
                        ],
                        'and',
                        [
                            ['available', '=', true],
                            'and',
                            ['price', '<', 1300],
                        ]
                    ]
                }
            }
        });
    </script>
</head>
<body>
    <smart-query-builder id="queryBuilder"></smart-query-builder>
</body>
</html>

Demo

Operation Icons

Icons can be shown next to labels in the coparison operation box by setting the property showIcons to true. The icons property, on the other hand, defines CSS classes to be applied to each of the built-in operations. Icons for these classes are applied in the smart-query-builder style sheet.

Demo

To apply a custom icon to a particular operation, add CSS that defines the icon's appearance:

<style type="text/css">
    .custom-equals-icon:after {
        background-image: url("https://img.icons8.com/officel/16/000000/equal-sign.png");
    }
</style>

then set the respective icons field (in this case, '=', the Equals operation) to the name of the class defined in the CSS:

document.getElementById('queryBuilder').icons['='] = 'custom-equals-icon';