Cascadeview Cascade Component Implementation ideas (separation ideas and single linked list) _javascript skills

Source: Internet
Author: User
Tags comments eventbase

This article introduces oneself to do the similar cascade function of the provincial and municipal cascade to realize the idea, in order to do as much as possible segregation of duties and performance and behavior separation, this function split into 2 components and the single linked list to achieve the key cascading logic, the next section has a demonstration of the GIF image. Although this is a very common function, but the realization of the logic of this article is clear, good understanding of the code, separated from the provinces and cities such semantics, taking into account the separation of performance and behavior, I hope the content of this article for your work to bring some reference value, welcome to read and correct.

Cascade Cascade operation

Cascadetype. PERSIST cascade Persistence (Save) operation

Cascadetype. Merge cascade Update (merge) operation

Cascadetype. Refresh Cascade refresh operation, only query fetch operation

Cascadetype. Remove Cascade Delete operation

Cascadetype. All cascade above all operations

Fetch Crawl is deferred loading, by default one side is loaded immediately, and many side is deferred loading

Mappedby Relationship Maintenance

Mappedby= "ParentID" represents the ParentID property in the children class to maintain the relationship, which must be exactly the same as the Children property name in the ParentID class.

Also note that the collection type in the parent class must be a list or set and cannot be set to ArrayList, otherwise it will be an error

Demo effect (code download, note: This effect requires HTTP to run, the other effect of the data is analog data, not back to the real return, so see the provinces and counties of the Drop-down data are the same):

Note: This article is used in the previous several related blog technology implementation, if necessary, you can click on the following link to understand:

1 Detailed JavaScript Inheritance implementation: Provides a class.js, used to define JavaScript class and build class inheritance;

2 The jquery technique allows any component to support event management similar to DOM: provides a eventbase.js for any component instance to provide DOM-like event management capabilities;

3 Ajax for jquery two encapsulation and Ajax cache Proxy components: Ajaxcache: Provide ajax.js and ajaxcache.js, simplify the Ajax invocation of jquery, and the client-side caching agent for the request.

Here is a detailed understanding of the requirements of this feature.

1. Functional analysis

This feature is illustrated with a cascade component that contains three cascading items:

1 Each cascading entry may require an option to be used as an input hint:

In this case, you can select an empty option (that is, the one that prompts the input) for each cascading item's data list:

You may not need option to use as an input hint:

In this case, only data option is selected in the data list for each cascading item, and no empty option is selected:

2 If the current page is queried from the database to have a value for the field corresponding to the Cascade component, then the value of the query is returned to the Cascade component:

If the corresponding field in the query does not have a value, it is shown in the 2 scenarios described in the 1th point requirement.

3 each cascade item in the data structure of the form of a single linked list of relations, the next Cascade item data list, the previous cascading items selected data have associated.

4 Considering the performance problems, the data lists of the cascading items are displayed with Ajax asynchronous loading.

5 automatically loads the list of first cascading items after the Cascade component initialization completes.

6 when a current cascade item changes, empty the list of data for all subsequent cascading items that are directly or indirectly associated, and automatically load the next cascading item's data list that is directly associated with the previous cascading item if the value is not empty. When emptying the list of data for a cascading item, be aware that if the cascading item needs to display the option of the input prompt, the option must be retained at the time of emptying.

7 to fully consider performance issues, to avoid repeated loading.

8) Taking into account the problem of form submission, when any cascade component is changed, it is necessary to reflect the value of the Cascade component to a hidden text field so that the value of the Cascade component can be submitted to the background through the text field.

function is roughly as above.

2. Realize the idea

1) Data structure

Cascading components are not the same as other components, it has some dependencies with the background data, I think the better implementation of the data structure is:

{
"id": 1,
"text": "Beijing",
"code": 110000,
"ParentID": 0
},
{
"id": 2,
"text": "Hebei Province" ,
"code": 220000,
"ParentID": 0
},
{
"id": 3,
"text": "Henan Province",
"code": 330000,
"ParentID": 0
}

ID is the unique identification of the data, the correlation between the data is built by ParentID, Text,code This is a common business field. If you press this data structure, the interface that we query the list of cascading items becomes very simple:

Check the list of the first cascading items
/api/cascade?parentid=0
/////Check the list of the second cascading items based on the value selected by the first cascading item
/api/cascade?parentid=1
// Check the list of the third cascading item, based on the value selected by the second cascading item
/api/cascade?parentid=4

This structure is also very good for the background, although in the structure they are a tree-shaped table structure, but the query is a single layer, so very good implementation.

As can be seen from the previous query demo, this structure can easily help us to unify the interface and parameters of the data query into one, which is a convenient thing for component development. After we get this data structure from the backstage, we parse each data into an option, such as <option value= "Beijing" data-param-value= "1" > Beijing </option> This will not only complete the Drop-down display of the data list, but also collect the values selected by the current cascading entry by selecting the form element, and finally, when the cascading items change, the selected option can be obtained. Take the value of the data-param-value stored on it as the ParentID parameter to load the next cascading item list. This is the Cascade Component data query and analysis of ideas.

But it also needs to consider the issue of flexibility, in the actual project, the possible cascade components of the data structure is by ID parentid this kind of similar correlation relationship definition, but their fields are not necessarily called ID parentid text code, most likely the other fields. That is to say: When parsing the data into option, option text also has value in what field to parse, and data-param-value this attribute with what field value, are uncertain and the name of the parameter used to query the data parentid can't be dead, either. Sometimes if the backstage staff first wrote the query interface, using other names, you can not ask someone else to change his parameter name, because he has to compile and then deploy, compared to the front-end more troublesome; and parentid=. 0 This 0 value is also not fixed, because the actual project in the first layer of data parentid may be empty, it may be-1. These things have to be designed as option, on the one hand to provide the default value, while leaving the outside to set according to the actual situation, such as this article in the final implementation of this option is defined:

TextField: ' text ', the name of the field to be displayed in the <option> element in the returned data
Valuefield: ' text ', the name of the field to be set on the value of the <option> element in the returned data
Paramfield: ' id ',////When the data query interface is invoked, the field name that corresponds to the data to be passed to the background
ParamName: ' ParentID ',///when calling the data query interface, the name of the parameter that passes the data after the URL
Defaultparam: ',//When querying the first cascading item, the value passed to the background, typically 0, ', or-1, which means to query the upper-level data

2) HTML structure

According to the 1th of the previous functional analysis, the initial HTML structure of the cascading component has 2 kinds:

<ul id= "Licenselocation-view" class= "Cascade-view clearfix" >
<li>
<select class= " Form-control ">
<option value=" > Please select the province </option>
</select>
</li>
< li>
<select class= "Form-control" >
<option value= "" > Please select City </option>
</select >
</li>
<li>
<select class= "Form-control" >
<option value= "" > Please select Counties </option>
</select>
</li>
</ul>

Or

<ul id= "Companylocation-view" class= "Cascade-view clearfix" >
<li>
<select class= " Form-control ">
</select>
</li>
<li>
<select class=" Form-control " >
</select>
</li>
<li>
<select class= "Form-control" >
</ select>
</li>
</ul>

The only difference between the two structures is whether option is configured to use as an input hint. Also note that if this empty option is required, the value attribute must be set to NULL, otherwise this empty option will submit the option message to the background when the form is submitted.

The most critical of these two structures is the select element, with no relationship between UL and Li, UL and Li are used for the UI; The select element has no semantics, no need to identify which is the province, which is the city, which is the county. Functionally, a select represents a cascading item, where the definition of the select is unimportant, we just tell the Cascade component, which select elements the Cascade item is composed of, and the only thing that needs to be told is the sequence of these select elements, But this is usually controlled by the default order of elements in HTML. This structure can help us to isolate the function of the component as much as possible from the performance and behavior.

3 Separation of duties and application of single linked list

As can be seen from the previous section, this cascade component, if divided by responsibility, can be divided into two core components, one responsible for overall functionality and internal cascading items (Cascadeview), and the other for the cascading item's functional implementation (Cascadeitem). In addition, in order to realize cascading logic more easily, we just need to link all the cascading items through the list, through publish-subscribe mode, the latter cascade item subscribes to the message that the previous cascading item changed, and when the preceding cascading item changes, the message is posted, and the subsequent cascading items are notified to handle the related logic. , the message may be passed to the last cascading item, through the use of a linked list. This is roughly the way it is described in a picture:

All we need to do is control the release and delivery of good news.

4 form Submission

In order to easily submit the value of a cascade component to the background, the entire cascading component can be treated as a whole, providing a onchanged event externally that can be used to get the value of all cascading items. Because there are multiple cascading items, this event can only be triggered when any cascading item is changed when the OnChanged event is published.

5) Ajax Cache

In this component you have to consider two levels of Ajax caching, the first is the component of this level, for example, I switched the first cascade to Beijing, this time the second cascade to the Beijing data loaded out, and then I put the first cascade from Beijing to Hebei and then switch to Beijing, This time the second cascading item to show is the list of Beijing's associated data, if we cache its data when we first load the list, this time we don't have to launch an AJAX request; the second is the level of Ajax request, if there are multiple cascading components on the page, I'm going to switch first cascade of first cascade components to Beijing, the browser initiates an AJAX request to load the data, and when I switch the first cascade of the second Cascade component to Beijing, the browser will send another request to load the data if I take the first component of the first AJAX request to return the data, Cached first, when the second component, with the same parameters to request the same interface, directly take the previous cache feel the result returned, so as to reduce an AJAX request. The second level of Ajax caching relies on the two-time encapsulation of jquery Ajax and the Ajax Caching Agent component: Ajaxcache, which, for components, implements only the first level of caching, but it does not take into account the second level of caching, Because the second level of cache implementation is transparent to it, it does not know that the AJAX components it uses have caching capabilities.

3. Implementation Details

The final implementation consists of three components, Cascadeview, Cascadeitem, Cascadepublicdefaults, the front two are the core of the component, and the last one is only used to define some option, Its function is described in detail in the Cascadeitem annotation. In addition, in the following code there are very detailed comments to explain the role of some key code, combined with the previous requirements to look at the code, it should be relatively easy to understand. I used to be inclined to use words to explain some of the implementation details, later, I slowly feel that this way a little thankless, the first is the details of the language is not good organization, sometimes the words do not express, obviously want to explain a thing clearly, the result is more confused, at least I read the things I wrote will be so feeling Second, developers have the ability to read the source code, and most of the active developers are willing to think of other people's coding to understand the implementation of ideas; So I use annotations to illustrate implementation details:

Cascadepublicdefaults:

Define (function () {
return {
URL: ',//Data query Interface
TextField: ' Text ', field name to be displayed within <option> element in returned data
Valuefield: ' text ', the name of the field in the returned data to be set on the value of the <option> element
Paramfield: ' id ',//when the data query interface is invoked, The field name to be passed to the background data
paramname: ' ParentID ',////When the data query interface is invoked, the name of the parameter that passes the data after the URL
defaultparam: '////When the first cascading item is queried, The values passed to the background, typically 0, ", or-1", means to query the upper-level data
keepfirstoption:true,//whether to retain the first option (used as input hint, such as: Please select Province), if true, when cascading items are reloaded, The default first option
resolveajax:function (res) {return
res
} is not cleared Because the Cascade item sends an asynchronous request when the data is loaded, the callback is used to parse the response returned by the asynchronous request
}
};

Cascadeview:

Define (function (Require, exports, module) {var $ = require (' jquery '); var Class = require (' Mod/class '); var eventbase =
Require (' mod/eventbase ');
var publicdefaults = require (' mod/cascadepublicdefaults ');
var Cascadeitem = require (' Mod/cascadeitem '); /** * publicdefaults role See Cascadeitem component comments/var DEFAULTS = $.extend ({}, Publicdefaults, {$elements: Undefined,//Cascade Item JQ Object Array, the order of elements in the data represents the cascading sequence Valueseparator: ', ',///Get the delimiter used for the value of all cascading items, if it is an English comma, the return value is shaped like Beijing, district, Chaoyang District values: ',//
A valueseparator delimited string representing the value of each select at the beginning onchanged: $.noop//Triggers this event when the value of any cascading item changes;  var Cascadeview = Class ({instancemembers: {init:function (options) {/) invokes the Init method Eventbase () of the parent class This.base () by This.base; var opts = This.options = this.getoptions (options), items = This.items = [], that is = this, $elements = opts. $elements, values
= Opts.values.split (Opts.valueseparator);
This.on (' Changed.cascadeview ', $.proxy (opts.onchanged, this)); $elements && $elements. Each (function (i) {var $el = $ (this);//Instantiate Cascadeitem component, and point the Previtem property of each instance to the previous instance//First Previtem property set to undefined var cascadeitem = new Cascadeitem ($el, $.extend (
That.getitemoptions (), {previtem:i = = 0? undefined:items[i-1], Value: $.trim (values[i)));
Items.push (Cascadeitem); A change in each Cascade item instance triggers the Changed event//external of the Cascadeview component to handle the business logic within the callback (for example, the value of all cascading items is set to a hidden field for form submission Cascadeitem.on ('
Changed.cascadeitem ', function () {That.trigger (' Changed.cascadeview ', That.getvalue ());});
});
Initialization completes automatic loading of first cascading items Items.length && items[0].load ();  }, Getoptions:function (options) {return $.extend ({}, This.getdefaults (), options); Getdefaults:function () {return
DEFAULTS; }, Getitemoptions:function () {var opts = {}, _options = This.options; for (var i in publicdefaults) {if (Publicdefault
S.hasownproperty (i) && i in _options) {opts[i] = _options[i];}
return opts; },//Gets the value of all cascading items, is a valueseparator delimited string//null value of the cascading item does not return Getvalue:function () {var value = []; This.items.forEach (functio N (item) {var val = $.trim (Item.getvalue ()); vAl!= ' && Value.push (val);
});
Return Value.join (This.options.valueSeparator);
}}, extend:eventbase});
return cascadeview; });

Cascadeitem:

Define (function (Require, exports, module) {var $ = require (' jquery '); var Class = require (' Mod/class '); var eventbase =
Require (' mod/eventbase ');
var publicdefaults = require (' mod/cascadepublicdefaults ');
var Ajaxcache = require (' Mod/ajaxcache ');
This is a cacheable ajax component var ajax = new Ajaxcache (); /** * A portion of the option is defined in the Publicdefaults because the Cascadeitem component will not be used externally directly with the Cascadeview component, so a portion of the option must become public, When the Cascadeview component also defines a * external Cascadeview component that passes all option * Cascadeview internal instantiation Cascadeitem, Then the option in Publicdefaults is passed to Cascadeitem/var DEFAULTS = $.extend ({}, Publicdefaults, {previtem:undefined,//point to previous cascading item V
Alue: '//initial display value} '; var Cascadeitem = Class ({instancemembers: {init:function ($el, Options) {//through This.base invoke the Init method Eventbase of the parent class This.base ($
EL);
this. $el = $el;
This.options = this.getoptions (options); This.previtem = This.options.prevItem; The previous cascading item this.hascontent = false;//This variable is used to control whether the data needs to be reloaded This.cache = {};//is used to cache data var that = this;//proxy Change event $el for the SELECT element. On (' Change ', function () {That.trigger (' Changed.cascadeitem ');}); When the value of the current cascading item changes, the processing of the data to be emptied and reloaded as needed This.previtem && this.prevItem.on (' Changed.cascadeitem ', function () {/
/As long as the previous value changes and has its own content, you have to clear the content that.hascontent && that.clear ();
If it is not the first cascading item and the previous cascading item does not select a valid option, the IF (That.previtem && $.trim (that.prevItem.getValue ()) = = "") return is not processed;
That.load ();
});
var value = $.trim (This.options.value); Value!== ' && this.one (' Render.cascadeitem ', function () {//Set initial value that. $el. Val (Value.split (', '));
Notifies subsequent cascading items to do the processing of emptying and reloading data that.trigger (' Changed.cascadeitem ');
});  }, Getoptions:function (options) {return $.extend ({}, This.getdefaults (), options); Getdefaults:function () {return
DEFAULTS; }, Clear:function () {var $el = this. $el; $el. Val ("); if (this.options.keepFirstOption) {//Retain the first option $el. Children ().
Filter (': GT (0) '). Remove ();
else {//empty all $el. html (');}//Notification of cascading items after the process of emptying and reloading data this.trigger (' Changed.cascadeitem '); This.hascontent = false;//indicates that the content is empty}, Load:fuNction () {var opts = this.options, paramvalue, that = this, DataKey;//datakey is the key name used in cache caching//Because the data for the first cascading item is the top-level data, so in the cache
With a fixed and unique key: root//The data cache of other cascading items with the key name in front of a selected option about if (!this.previtem) {paramvalue = Opts.defaultparam; datakey = ' root '; else {paramvalue = This.prevItem.getParamValue (); datakey = paramvalue}//See if there are any loaded data in the data cache, show it directly and avoid Ajax if (Datake
Y in This.cache) {This.render (This.cache[datakey]);} else {var params = {}; params[opts.paramname] = paramvalue; Ajax.get (Opts.url, params). Done (function (res) {//resolveajax This callback is used to parse the data returned by Ajax externally//It needs to return an data array of var data =
Opts.resolveajax (RES);
if (data) {That.cache[datakey] = data; That.render (data);}}); }, Render:function (data) {var html = [], opts = This.options Data.foreach (function (item) {Html.push ([' <option V Alue= "', Item[opts.valuefield], '" data-param-value= ",//to store the Paramfield corresponding value on the option's Data-param-value attribute item[
Opts.paramfield], ' > ', Item[opts.textfield], ' </option> '].join (') '); Using AppenD to dynamically add, to avoid affecting the first option//and finally to set the value to null this. $el. Append (Html.join (')). Val (');
this.hascontent = true;//denotes content this.trigger (' Render.cascadeitem '); Getvalue:function () {return this. $el. Val ()}, Getparamvalue:function () {return this. $el. Find (' option:selected ').
Data (' paramvalue ');
}}, extend:eventbase});
return cascadeitem; });

4. Demo description

The structure of the demo code:

One of the boxes is the related part of the demo. Html/regist.html is the demo effect of the page, Js/app/regist.js is the entrance of the demo effect JS:

Define (function (Require, exports, module) {var $ = require (' jquery ');
  var Cascadeview = require (' Mod/cascadeview '); function Publicsetcascadeview (FieldName, opts) {This.cascadeview = new Cascadeview ({$elements: $ (' # ' + Fieldna Me + '-view '). Find (' select '), URL: '. /api/cascade.json ', onChanged:this.onChanged, Values:opts.values, KeepFirstOption:this.keepFirstOptio
        N, resolveajax:function (res) {if (Res.code = =) {return res.data;
  }
      }
    });  var location_views = {licenselocation: {$input: $ (' input[name= ' licenselocation] '), Keepfirstoption: True, Setcascadeview:publicsetcascadeview, Onchanged:function (e, value) {Location_views.licenseloca
      tion. $input. val (value); }, Companylocation: {$input: $ (' input[name= "companylocation"]), Keepfirstoption:false, setc Ascadeview:publicsetcascadeview, Onchanged:function (E, ValUE) {location_views.companylocation. $input. val (value);
  }
    }
  }; LOCATION_VIEWS.licenseLocation.setCascadeView (' Licenselocation ', {values:location_views.licenselocation.$
  Input.val ()}); LOCATION_VIEWS.companyLocation.setCascadeView (' Companylocation ', {values:location_views.companylocation.$
Input.val ()}); });

Notice the location_views of this variable in the above code, because there are multiple cascading components on the page, and this variable is actually managed in a similar way through a policy pattern that relates everything to each component. If you do not, it is easy to generate duplicate code, this form is also more conducive to the entry file this processing business logic, where some business logic separation and encapsulation.

5. Others

This is estimated to be the last blog written in the company today, after two days to go to the new unit to work, not sure whether there are so many spare time to record the usual work ideas, but somehow has cultivated the habit of blogging, in the future will not have time to squeeze out the time. This year's goal is mainly to broaden the knowledge, improve the quality of the code, follow-up blog more or in the component development of this category, hope to be able to get everyone to continue to pay attention to cloud Habitat community site!

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.