Transformation of Combo Select supports server-side fuzzy search and combo fuzzy search
Combo select is used in the project. The fuzzy search function is added for the default select statement and runs well all the time.
1. Problems Encountered
However, we recently encountered a select statement with a large data volume: more than 2000 data items are initialized and loaded. We use ajax to read all option json, and js traverses it in the browser to generate the complete html. When the data size increases, there will be significant loss between ajax reading data and browser processing data. Page initialization takes a long time to reduce user friendliness.
2. Alternative Solutions
After a brief analysis, we thought of three possible solutions.
2.1 modify data structure
Currently, there are more than 2000 data records at the same level, which can be logically split into two levels. In this way, after the data is split into two levels, two associated Select statements can be used to greatly reduce the number of options loaded by each select statement.
2.2 Use redis to cache data
Because distributed deployment is adopted, the data is actually transmitted between servers multiple times. A large amount of data increases the time consumed for each level of transmission, making it difficult to accept the final time consumed.
Data can be cached on the api server using nosql, which can reduce the time consumption to a certain extent.
2.3 modify the combo select plug-in
Starting from the front-end, select only displays a small amount of data. when you enter a keyword for search, it is loaded from the server in real time. This method increases the number of calls, but can greatly reduce the amount of data and shorten the page loading time.
All three solutions can solve the problem to a certain extent. We decided to first try from the combo select plug-in. If the effect cannot be achieved, we should consider the redis cache or data structure modification solution.
3 Combo Select Code Analysis
URL https://github.com/PebbleRoad/combo-select, thanks for providing such an excellent plug-in.
3.1 basic usage
First, build a select statement on the page, initialize the option data, and then call the script
$ ("# SelectId"). comboSelect (); |
For more complex functions, go to the official website.
3.2 html Structure
During Combo Select execution, a <div class = "combo-select"> is nested in the outer layer of the original select statement, and three elements are added after the select statement.
Div. combo-arrow, which is the drop-down arrow
Ul. combo-dropdown is the drop-down list used to display
Input. combo-input is the input box used to input the fuzzy search content.
And hide it by modifying the attributes of the original select statement.
3.3 js Data Model
During combo select initialization, several attributes are constructed after a series of code:
$ Container: generate a new div and place the original select statement and the new ul statement in it.
$ El: initial select element
$ Options: All option data
$ Dropdown: generated ul. combo-dropdown object
$ Items: Convert all options to data in the li format.
Is the relationship between the data model and html elements.
3.4 plug-in Initialization
The code function Plugin (element, options) of the js plug-in initializes the plug-in, adjusts the html elements and initializes the js data model based on the current select data. The initialization process is as follows:
3.5 fuzzy query Logic
When a user inputs text in the input, the keydown and keyup events are triggered. In the keyup event, the data in $ items is matched in sequence and the visible attribute is set, display part of the data.
In this process, the original select ($ el) and all its options ($ options) remain unchanged. The drop-down list changes mainly by ul. li ($ items) is set to visible or invisible.
4. Change to the Server-side Real-Time query Solution
The overall modification scheme is solved in three aspects: Server API, js component, and frontend call.
4.1 Server API Modification
The Server must provide an interface for fuzzy search by name. Note that the maximum number of returned data records must be set. Avoid returning a large amount of data based on the query conditions, thus losing the advantage of the solution.
After limiting the maximum number of entries, You need to clearly introduce this implementation logic with the product. If the keywords entered by the user are not differentiated, you may not be able to find the desired data; in this case, you need to enter keywords with more differentiation.
4.2 ComboSelect component modification 4.2.1 Modification Scheme
Logic for modifying the keyup event: ul was set separately. li is visible, changed to reload all options of select, and rebuilt $ items according to options, and set it to all ul. li is visible.
4.2.2 add several parameters for the component
Entity: 'entity ', ItemName: 'itemname ', CurItemField: 'curitemcode ', CurItemValue :'', CurItemName :'', Url :'', Limit: 7 |
- Entity: the data type currently being processed. This is used to adapt to the differences in json definitions returned by different APIs. A better solution is to require that all data types use the same attribute name. The work und is to add this entity and perform differentiated processing on js. This reduces the versatility of the transformation.
- ItemName: parameter name of the user input value required to call the api
- CurItemField: Specifies the input name of an item in html.
- CurItemValue: the value of the selected data.
- CurItemName: the title of the selected data
- Limit: The page size of the Server api fuzzy search Return Value
4.2.3 modify the _ filter () method to implement server-side fuzzy query
Modify the method of the original component to determine whether the server-side refresh url is set. If this parameter is not set, the original logic is used. If this parameter is set, perform a fuzzy query based on user input, generate all the options hidden in the browser, and update them to $ dropdown.
If (self. settings. url! = ''){ // Prepare the json data required to call the api Var self = this; Var ajaxData = { "Paging": true, "Offset": 0, "Limit": self. settings. limit }; If (self. settings. itemName! = ''& Needle! = ''){ AjaxData [self. settings. itemName] = needle; } If (self. settings. curItemField! = ''& Self. settings. curItemValue! = ''){ AjaxData [self. settings. curItemField] = self. settings. curItemValue; } // Query data from the server $. Ajax ({ Url: self. settings. url, Type: 'post ', Data: ajaxData, Success: function (data ){ Var obj = $. parseJSON (data ); // Delete the original select data and traverse the query results to generate an option to add it to the select statement. Var dropdownHtml = '', k = 0, p = ''; Self. $ el. empty (); Self. $ el. append ("<option value =''> select </option> "); Var confirmedValue; Self.$dropdown.html ("<li class = 'option-item' data-index = '0' data-value =''> select </li> "); For (var I = 0; I <obj. length; I ++ ){ Var itemCode; Var itemName; Var itemExtraCode; If (self. settings. entity = 'entity '){ ItemCode = obj [I]. entityCode; ItemName = obj [I]. entityName; ItemExtraCode = obj [I]. entityShortName; } Else { ItemCode = obj [I]. itemCode; ItemName = obj [I]. itemName; ItemExtraCode = itemCode; } // Generate select option Var oneOption = $ ("<option> </option> "); $ (OneOption). val (itemCode ); Detail (oneoptionapps.html (itemName ); If (itemCode = self. settings. curItemValue | itemExtraCode = self. settings. curItemValue | itemName = self. settings. curItemName ){ $ (OneOption). attr ("selected", "selected "); Self. settings. curItemValue = itemCode; ConfirmedValue = itemCode; } Self. $ el. append (oneOption ); If (confirmedValue! = Undefined & confirmedValue! = ''){ Self. $ el. val (confirmedValue ); } // Generate li in $ dropdown Var oneItem = $ ("<li> </li> "); $ (OneItem). attr ("class", this. disabled? Self. settings. disabledClass: "option-item "); $ (OneItem). attr ("data-index", (I + 1 )); $ (OneItem). attr ("data-value", itemCode ); Detail (oneitemdetail .html (itemName ); Self. $ dropdown. append (oneItem ); } // Assign a value for $ items Self. $ items = self. $ dropdown. children (); // Trigger subsequent open methods Self. $ container. trigger ('comboselect: open ') } }); } |
4.2.4 modify init () for first loading
The code is similar to _ filter (). An independent method should be provided to call the two methods.
Init: function (){ Var self = this; If (self. settings. url! = ''){ // Dynamically refresh the code ...... } Else { // Original component Logic Self. _ construct (); Self. _ events (); } }, |
4.3 frontend call 4.3.1 add parameters in html code
Use the comboselect-prefix, as shown in figure
<Select class = "list-filedV" id = "entityCode" name = "entityCode" Comboselect-entity = "entity" onchange = "getBranch ('') "> </Select> <Input type = 'siden' name = 'entityname' id = 'entityname'> |
4.3.2 create a combobox without default values in js
Complete initialization in js Code, Code
// Obtain data FunctionGetEntityData (){ $ ("# EntityCode"). comboSelect ({ "ItemName": "entityName ", "Url": contextPath + "/new/dictionary/searchEntityData. ajax ", "Limit": 7 }); } |
4.3.3 js-generated combobox with default values
It is common on the editing page.
// Get data _ modify FunctionGetEntityDataUp (curEntityCode, curEntityName ){ $ ("# EntityCode"). comboSelect ({ "ItemName": "entityName ", "CurItemField": "includeEntityCode ", "CurItemValue": curEntityCode, "CurItemName": curEntityName, "Url": contextPath + "/new/dictionary/searchEntityData. ajax ", "Limit": 7 }); } |
5. Reduce the frequency of calling servers
Note that the _ keyup code refresh data every time you press the key (excluding special characters ignored by this function. If data is filtered in the browser, the problem is not obvious. However, each fuzzy query goes through the Server query, resulting in a large amount of api access.
5.1 change plan
In _ keyup (), call _ delayFilter () to trigger the modified _ filter () method.
5.2 code_delayfilter ()
This. filterTimer = 0; _ DelayFilter: function (search ){ If (this. filterTimer> 0 ){ ClearTimeout (this. filterTimer ); This. filterTimer = 0; } Var self = this; This. filterTimer = setTimeout (function (){ Self. _ filter (search ); },500 ); }, |