亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

CascadeView級(jí)聯(lián)組件實(shí)現(xiàn)思路詳解(分離思想和單鏈表)

 更新時(shí)間:2016年04月12日 11:10:02   作者:流云諸葛  
本文介紹自己最近做省市級(jí)聯(lián)的類似的級(jí)聯(lián)功能的實(shí)現(xiàn)思路,為了盡可能地做到職責(zé)分離跟表現(xiàn)與行為分離,這個(gè)功能拆分成了2個(gè)組件并用到了單鏈表來實(shí)現(xiàn)關(guān)鍵的級(jí)聯(lián)邏輯,下一段有演示效果的gif圖

本文介紹自己最近做省市級(jí)聯(lián)的類似的級(jí)聯(lián)功能的實(shí)現(xiàn)思路,為了盡可能地做到職責(zé)分離跟表現(xiàn)與行為分離,這個(gè)功能拆分成了2個(gè)組件并用到了單鏈表來實(shí)現(xiàn)關(guān)鍵的級(jí)聯(lián)邏輯,下一段有演示效果的gif圖。雖然這是個(gè)很常見的功能,但是本文的實(shí)現(xiàn)邏輯清晰,代碼好理解,脫離了省市級(jí)聯(lián)這樣的語義,考慮了表現(xiàn)與行為的分離,希望本文的內(nèi)容能夠?yàn)槟愕墓ぷ鲙硪恍﹨⒖嫉膬r(jià)值,歡迎閱讀和指正。

Cascade 級(jí)聯(lián)操作

CascadeType. PERSIST 級(jí)聯(lián)持久化 ( 保存 ) 操作

CascadeType. MERGE 級(jí)聯(lián)更新 ( 合并 ) 操作

CascadeType. REFRESH 級(jí)聯(lián)刷新操作,只會(huì)查詢獲取操作

CascadeType. REMOVE 級(jí)聯(lián)刪除操作

CascadeType. ALL 級(jí)聯(lián)以上全部操作

Fetch 抓取是否延遲加載,默認(rèn)情況一的方為立即加載,多的一方為延遲加載

mappedBy 關(guān)系維護(hù)

mappedBy= "parentid" 表示在children 類中的 parentid 屬性來維護(hù)關(guān)系,這個(gè)名稱必須和children 類中的 parentid屬性名稱完全一致才行。

另外需要注意,parent類中的集合類型必須是List或者Set,不能設(shè)置為ArrayList,否則會(huì)報(bào)錯(cuò)

演示效果(代碼下載,注:該效果需要http才能運(yùn)行,另外效果中的數(shù)據(jù)是模擬數(shù)據(jù),并不是后臺(tái)真實(shí)返回的,所以看到的省市縣的下拉數(shù)據(jù)都是一樣的):

注:本文用到了前面幾篇相關(guān)博客的技術(shù)實(shí)現(xiàn),如果有需要的話可以點(diǎn)擊下面的鏈接前去了解:

1)詳解Javascript的繼承實(shí)現(xiàn):提供一個(gè)class.js,用來定義javascript的類和構(gòu)建類的繼承關(guān)系;

2)jquery技巧之讓任何組件都支持類似DOM的事件管理:提供一個(gè)eventBase.js,用來給任意組件實(shí)例提供類似DOM的事件管理功能;

3)對(duì)jquery的ajax進(jìn)行二次封裝以及ajax緩存代理組件:AjaxCache:提供ajax.js和ajaxCache.js,簡(jiǎn)化jquery的ajax調(diào)用,以及對(duì)請(qǐng)求進(jìn)行客戶端的緩存代理。

下面先來詳細(xì)了解下這個(gè)功能的要求。

1. 功能分析

以包含三個(gè)級(jí)聯(lián)項(xiàng)的級(jí)聯(lián)組件來說明這個(gè)功能:

1)每個(gè)級(jí)聯(lián)項(xiàng)可能需要一個(gè)用作輸入提示的option:

這種情況每個(gè)級(jí)聯(lián)項(xiàng)的數(shù)據(jù)列表中都能選擇一個(gè)空的option(就是輸入提示的那個(gè)):

也可能不需要用作輸入提示的option:

這種情況每個(gè)級(jí)聯(lián)項(xiàng)的數(shù)據(jù)列表中只能選數(shù)據(jù)option,選不到空的option:

2)如果當(dāng)前這個(gè)頁面是從數(shù)據(jù)庫中查詢出來跟級(jí)聯(lián)組件對(duì)應(yīng)的字段有值,那么就把查詢出來的值回顯到級(jí)聯(lián)組件上:

如果查詢出來的對(duì)應(yīng)字段沒有值,那么就按第1)點(diǎn)需求描述的2種情況顯示。

3)各個(gè)級(jí)聯(lián)項(xiàng)在數(shù)據(jù)結(jié)構(gòu)上構(gòu)成單鏈表的關(guān)系,后一個(gè)級(jí)聯(lián)項(xiàng)的數(shù)據(jù)列表,跟前一個(gè)級(jí)聯(lián)項(xiàng)所選擇的數(shù)據(jù)有關(guān)聯(lián)的。

4)考慮到性能方面的問題,各個(gè)級(jí)聯(lián)項(xiàng)的數(shù)據(jù)列表都采用ajax異步加載顯示。

5)在級(jí)聯(lián)組件初始化完成以后,自動(dòng)加載第一個(gè)級(jí)聯(lián)項(xiàng)的列表。

6)當(dāng)前一個(gè)級(jí)聯(lián)項(xiàng)發(fā)生改變時(shí),清空后面所有直接或間接關(guān)聯(lián)的級(jí)聯(lián)項(xiàng)的數(shù)據(jù)列表,同時(shí)如果前一個(gè)級(jí)聯(lián)項(xiàng)改變后的值不為空則自動(dòng)加載跟它直接關(guān)聯(lián)的下一個(gè)級(jí)聯(lián)項(xiàng)的數(shù)據(jù)列表。清空級(jí)聯(lián)項(xiàng)的數(shù)據(jù)列表時(shí)要注意:如果級(jí)聯(lián)項(xiàng)需要顯示輸入提示的option,在清空的時(shí)候得保留該option。

7)要充分考慮性能問題,避免重復(fù)加載。

8)考慮到表單提交的問題,當(dāng)級(jí)聯(lián)組件任意級(jí)聯(lián)項(xiàng)發(fā)生改變后,得把級(jí)聯(lián)組件所選的值體現(xiàn)到一個(gè)隱藏的文本域內(nèi),方便把級(jí)聯(lián)組件的值通過該文本域提交到后臺(tái)。

功能大致如上。

2. 實(shí)現(xiàn)思路

1)數(shù)據(jù)結(jié)構(gòu)

級(jí)聯(lián)組件跟別的組件不太一樣的是,它跟后臺(tái)的數(shù)據(jù)有一些依賴,我考慮的比較好實(shí)現(xiàn)的數(shù)據(jù)結(jié)構(gòu)是:

{
"id": 1,
"text": "北京市",
"code": 110000,
"parentId": 0
},
{
"id": 2,
"text": "河北省",
"code": 220000,
"parentId": 0
},
{
"id": 3,
"text": "河南省",
"code": 330000,
"parentId": 0
}

id是數(shù)據(jù)的唯一標(biāo)識(shí),數(shù)據(jù)之間的關(guān)聯(lián)關(guān)系通過parentId來構(gòu)建,text,code這種都屬于普通的業(yè)務(wù)字段。如果按這個(gè)數(shù)據(jù)結(jié)構(gòu),我們查詢級(jí)聯(lián)項(xiàng)數(shù)據(jù)列表的接口就會(huì)變得很簡(jiǎn)單:

//查第一個(gè)級(jí)聯(lián)項(xiàng)的列表
/api/cascade?parentId=0
//根據(jù)第一個(gè)級(jí)聯(lián)項(xiàng)選的值,查第二個(gè)級(jí)聯(lián)項(xiàng)的列表
/api/cascade?parentId=1
//根據(jù)第二個(gè)級(jí)聯(lián)項(xiàng)選的值,查第三個(gè)級(jí)聯(lián)項(xiàng)的列表
/api/cascade?parentId=4

這個(gè)結(jié)構(gòu)對(duì)于后臺(tái)來說也很好處理,雖然在結(jié)構(gòu)上它們是一種樹形的表結(jié)構(gòu),但是查詢都是單層的,所以很好實(shí)現(xiàn)。

從前面的查詢演示也能夠看出,這個(gè)結(jié)構(gòu)能夠很方便地幫我們把數(shù)據(jù)查詢的接口和參數(shù)統(tǒng)一成一個(gè),這對(duì)于組件開發(fā)來說是一個(gè)很方便的事情。我們從后臺(tái)拿到這個(gè)數(shù)據(jù)結(jié)構(gòu)之后,把每一條數(shù)據(jù)解析成一個(gè)option,如<option value=”北京市” data-param-value=”1”>北京市</option>,這樣既能完成數(shù)據(jù)列表的下拉顯示,還能通過select這個(gè)表單元素的作用收集到當(dāng)前級(jí)聯(lián)項(xiàng)所選中的值,最后當(dāng)級(jí)聯(lián)項(xiàng)發(fā)生改變的時(shí)候,還能夠獲取到選中的option,把它上面存儲(chǔ)的data-param-value的值作為parentId這個(gè)參數(shù),去加載下一個(gè)級(jí)聯(lián)項(xiàng)的列表。這也是級(jí)聯(lián)組件數(shù)據(jù)查詢和解析的思路。

但是這里面還需要考慮的是靈活性的問題,在實(shí)際的項(xiàng)目中,可能級(jí)聯(lián)組件的數(shù)據(jù)結(jié)構(gòu)是按id parentId這種類似的關(guān)聯(lián)關(guān)系定義的,但是它們的字段不一定是叫id parentId text code,很有可能是別的字段。也就是說:在把數(shù)據(jù)解析成option的時(shí)候,option的text還有value到底用什么字段來解析,以及data-param-value這個(gè)屬性的用什么字段的值,都是不確定的;還有查詢數(shù)據(jù)時(shí)用的參數(shù)名稱parentId也不能是死的,有的時(shí)候如果后臺(tái)人員先寫好了查詢接口,用了別的名稱,你不可能要求人家去改他的參數(shù)名稱,因?yàn)樗沁吺切枰幾g再部署的,相比前端更麻煩一些;還有parentId=0這個(gè)0值也是不能固定,因?yàn)閷?shí)際項(xiàng)目中第一層的數(shù)據(jù)的parentid有可能是空,也有可能是-1。這些東西都得設(shè)計(jì)成option,一方面提供默認(rèn)值,同時(shí)留給外部根據(jù)實(shí)際情況來設(shè)置,比如本文最終的實(shí)現(xiàn)中這個(gè)option都是這樣定義的:

textField: 'text', //返回的數(shù)據(jù)中要在<option>元素內(nèi)顯示的字段名稱
valueField: 'text', //返回的數(shù)據(jù)中要設(shè)置在<option>元素的value上的字段名稱
paramField: 'id', //當(dāng)調(diào)用數(shù)據(jù)查詢接口時(shí),要傳遞給后臺(tái)的數(shù)據(jù)對(duì)應(yīng)的字段名稱
paramName: 'parentId', //當(dāng)調(diào)用數(shù)據(jù)查詢接口時(shí),跟在url后面?zhèn)鬟f數(shù)據(jù)的參數(shù)名
defaultParam: '', //當(dāng)查詢第一個(gè)級(jí)聯(lián)項(xiàng)時(shí),傳遞給后臺(tái)的值,一般是0,'',或者-1等,表示要查詢第上層的數(shù)據(jù)

2)html結(jié)構(gòu)

根據(jù)前面的功能分析的第1條,級(jí)聯(lián)組件的初始html結(jié)構(gòu)有2種:

<ul id="licenseLocation-view" class="cascade-view clearfix">
<li>
<select class="form-control">
<option value="">請(qǐng)選擇省份</option>
</select>
</li>
<li>
<select class="form-control">
<option value="">請(qǐng)選擇城市</option>
</select>
</li>
<li>
<select class="form-control">
<option value="">請(qǐng)選擇區(qū)縣</option>
</select>
</li>
</ul>

<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>

這兩個(gè)結(jié)構(gòu)唯一的區(qū)別就在于是否配置了用作輸入提示的option。另外需要注意的是如果需要這個(gè)空的option,一定得把value屬性設(shè)置成空,否則這個(gè)空的option在表單提交的時(shí)候會(huì)把option的提示信息提交到后臺(tái)。

這兩個(gè)結(jié)構(gòu)最關(guān)鍵的是select元素,跟ul和li沒有任何關(guān)系,ul跟li是為了UI而用到的;select元素沒有任何語義,不用去標(biāo)識(shí)哪個(gè)是省份,哪個(gè)是城市,哪個(gè)是區(qū)縣。從功能上來說,一個(gè)select代表一個(gè)級(jí)聯(lián)項(xiàng),這些select在哪定義都不重要,我們只要告訴級(jí)聯(lián)組件,它的級(jí)聯(lián)項(xiàng)由哪些select元素構(gòu)成就行了,唯一需要額外告訴組件的就是這些select元素的先后關(guān)系,但是這個(gè)通常都是用元素在html中的默認(rèn)順序來控制的。這個(gè)結(jié)構(gòu)能夠幫助我們把組件的功能盡可能地做到表現(xiàn)與行為分離。

3)職責(zé)分離和單鏈表的運(yùn)用

從前面的部分也差不多能看出來了,這個(gè)級(jí)聯(lián)組件如果按職責(zé)劃分,可以分成兩個(gè)核心的組件,一個(gè)負(fù)責(zé)整體功能和內(nèi)部級(jí)聯(lián)項(xiàng)的管理(CascadeView),另一個(gè)負(fù)責(zé)級(jí)聯(lián)項(xiàng)的功能實(shí)現(xiàn)(CascadeItem)。另外為了更方便地實(shí)現(xiàn)級(jí)聯(lián)的邏輯,我們只需要把所有的級(jí)聯(lián)項(xiàng)通過鏈表連起來,通過發(fā)布-訂閱模式,后一個(gè)級(jí)聯(lián)項(xiàng)訂閱前一個(gè)級(jí)聯(lián)項(xiàng)發(fā)生改變的消息;當(dāng)前面的級(jí)聯(lián)項(xiàng)發(fā)生改變的時(shí)候,發(fā)布消息,通知后面的級(jí)聯(lián)項(xiàng)去處理相關(guān)邏輯;通過鏈表的作用,這個(gè)消息可能可以一直傳遞到最后一個(gè)級(jí)聯(lián)項(xiàng)為止。用圖來描述的話,大致就是這個(gè)樣子:

我們需要做的就是控制好消息的發(fā)布跟傳遞。

4)表單提交

為了能夠方便地將級(jí)聯(lián)組件的值提交到后臺(tái),可以把整個(gè)級(jí)聯(lián)組件當(dāng)成一個(gè)整體,對(duì)外提供一個(gè)onChanged事件,外部可通過這個(gè)事件獲取所有級(jí)聯(lián)項(xiàng)的值。由于存在多個(gè)級(jí)聯(lián)項(xiàng),所以在發(fā)布o(jì)nChanged這個(gè)事件時(shí),只能在任意級(jí)聯(lián)項(xiàng)發(fā)生改變的時(shí)候,都去觸發(fā)這個(gè)事件。

5)ajax緩存

在這個(gè)組件里面得考慮兩個(gè)層級(jí)的ajax緩存,第一個(gè)是組件這一層級(jí)的,比如我把第一個(gè)級(jí)聯(lián)項(xiàng)切換到了北京,這個(gè)時(shí)候第二個(gè)級(jí)聯(lián)項(xiàng)就把北京的數(shù)據(jù)加載出來了,然后我把第一個(gè)級(jí)聯(lián)項(xiàng)從北京切換到河北再切換到北京,這個(gè)時(shí)候第二個(gè)級(jí)聯(lián)項(xiàng)要顯示的還是北京的關(guān)聯(lián)數(shù)據(jù)列表,如果我們?cè)诘谝淮渭虞d這個(gè)列表的時(shí)候就把它的數(shù)據(jù)緩存下來了,那么這次就不用發(fā)起ajax請(qǐng)求了;第二個(gè)是ajax請(qǐng)求這一層級(jí)的,假如頁面上有多個(gè)級(jí)聯(lián)組件,我先把第一個(gè)級(jí)聯(lián)組件的第一個(gè)級(jí)聯(lián)項(xiàng)切換到北京,瀏覽器發(fā)起一個(gè)ajax請(qǐng)求加載數(shù)據(jù),當(dāng)我再把第二個(gè)級(jí)聯(lián)組件的第一個(gè)級(jí)聯(lián)項(xiàng)切換到北京的時(shí)候,瀏覽器還會(huì)再發(fā)一個(gè)請(qǐng)求去加載數(shù)據(jù),如果我把第一個(gè)組件第一次ajax請(qǐng)求的返回的數(shù)據(jù),先緩存起來,當(dāng)?shù)诙€(gè)組件,用同樣的參數(shù)請(qǐng)求同樣的接口時(shí),直接拿之前緩存覺得結(jié)果返回,這樣也能減少一次ajax請(qǐng)求。第二個(gè)層級(jí)的ajax緩存依賴上文《對(duì)jquery的ajax進(jìn)行二次封裝以及ajax緩存代理組件:AjaxCache》,對(duì)于組件來說,它內(nèi)部只實(shí)現(xiàn)了第一個(gè)層級(jí)的緩存,但是它不用考慮第二個(gè)層級(jí)的緩存,因?yàn)榈诙€(gè)層級(jí)的緩存實(shí)現(xiàn)對(duì)它來說是透明的,它不知道它用到的ajax組件有緩存的功能。

3. 實(shí)現(xiàn)細(xì)節(jié)

最終的實(shí)現(xiàn)包含了三個(gè)組件,CascadeView、CascadeItem、CascadePublicDefaults,前面兩個(gè)是組件的核心,最后一個(gè)只是用來定義一些option,它的作用在CascadeItem的注釋里面有詳細(xì)的描述。另外在下面的代碼中有非常詳細(xì)的注釋解釋了一些關(guān)鍵代碼的作用,結(jié)合著前面的需求來看代碼,應(yīng)該還是比較容易理解的。我以前傾向于用文字來解釋一些實(shí)現(xiàn)細(xì)節(jié),后來我慢慢覺得這種方式有點(diǎn)費(fèi)力不討好,第一是細(xì)節(jié)層面的語言不好組織,有的時(shí)候言不達(dá)意,明明想把一件事情解釋清楚,結(jié)果反而弄得更加迷糊,至少我自己看自己寫的東西就會(huì)這樣的感觸;第二是本身開發(fā)人員都具有閱讀源碼的能力,而且大部分積極的開發(fā)人員都愿意通過琢磨別人的代碼來理解實(shí)現(xiàn)思路;所以我改用注釋的方式來說明實(shí)現(xiàn)細(xì)節(jié):)

CascadePublicDefaults:

define(function () {
return {
url: '',//數(shù)據(jù)查詢接口
textField: 'text', //返回的數(shù)據(jù)中要在<option>元素內(nèi)顯示的字段名稱
valueField: 'text', //返回的數(shù)據(jù)中要設(shè)置在<option>元素的value上的字段名稱
paramField: 'id', //當(dāng)調(diào)用數(shù)據(jù)查詢接口時(shí),要傳遞給后臺(tái)的數(shù)據(jù)對(duì)應(yīng)的字段名稱
paramName: 'parentId', //當(dāng)調(diào)用數(shù)據(jù)查詢接口時(shí),跟在url后面?zhèn)鬟f數(shù)據(jù)的參數(shù)名
defaultParam: '', //當(dāng)查詢第一個(gè)級(jí)聯(lián)項(xiàng)時(shí),傳遞給后臺(tái)的值,一般是0,'',或者-1等,表示要查詢第上層的數(shù)據(jù)
keepFirstOption: true, //是否保留第一個(gè)option(用作輸入提示,如:請(qǐng)選擇省份),如果為true,在重新加載級(jí)聯(lián)項(xiàng)時(shí),不會(huì)清除默認(rèn)的第一個(gè)option
resolveAjax: function (res) {
return res;
}//因?yàn)榧?jí)聯(lián)項(xiàng)在加載數(shù)據(jù)的時(shí)候會(huì)發(fā)異步請(qǐng)求,這個(gè)回調(diào)用來解析異步請(qǐng)求返回的響應(yīng)
}
});

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的作用見CascadeItem組件內(nèi)的注釋
*/
var DEFAULTS = $.extend({}, PublicDefaults, {
$elements: undefined, //級(jí)聯(lián)項(xiàng)jq對(duì)象的數(shù)組,元素在數(shù)據(jù)中的順序代表級(jí)聯(lián)的先后順序
valueSeparator: ',', //獲取所有級(jí)聯(lián)項(xiàng)的值時(shí)使用的分隔符,如果是英文逗號(hào),返回的值形如 北京市,區(qū),朝陽區(qū)
values: '', //用valueSeparator分隔的字符串,表示初始時(shí)各個(gè)select的值
onChanged: $.noop //當(dāng)任意級(jí)聯(lián)項(xiàng)的值發(fā)生改變的時(shí)候會(huì)觸發(fā)這個(gè)事件
});
var CascadeView = Class({
instanceMembers: {
init: function (options) {
//通過this.base調(diào)用父類EventBase的init方法
this.base();
var opts = this.options = this.getOptions(options),
items = this.items = [],
that = 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);
//實(shí)例化CascadeItem組件,并把每個(gè)實(shí)例的prevItem屬性指向前一個(gè)實(shí)例
//第一個(gè)prevItem屬性設(shè)置為undefined
var cascadeItem = new CascadeItem($el, $.extend(that.getItemOptions(), {
prevItem: i == 0 ? undefined : items[i - 1],
value: $.trim(values[i])
}));
items.push(cascadeItem);
//每個(gè)級(jí)聯(lián)項(xiàng)實(shí)例發(fā)生改變都會(huì)觸發(fā)CascadeView組件的changed事件
//外部可在這個(gè)回調(diào)內(nèi)處理業(yè)務(wù)邏輯
//比如將所有級(jí)聯(lián)項(xiàng)的值設(shè)置到一個(gè)隱藏域里面,用于表單提交
cascadeItem.on('changed.cascadeItem', function () {
that.trigger('changed.cascadeView', that.getValue());
});
});
//初始化完成自動(dòng)加載第一個(gè)級(jí)聯(lián)項(xiàng)
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 (PublicDefaults.hasOwnProperty(i) && i in _options) {
opts[i] = _options[i];
}
}
return opts;
},
//獲取所有級(jí)聯(lián)項(xiàng)的值,是一個(gè)用valueSeparator分隔的字符串
//為空的級(jí)聯(lián)項(xiàng)的值不會(huì)返回
getValue: function () {
var value = [];
this.items.forEach(function (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');
//這是一個(gè)可緩存的Ajax組件
var Ajax = new AjaxCache();
/**
* 有一部分option定義在PublicDefaults里面,因?yàn)镃ascadeItem組件不會(huì)被外部直接使用
* 外部用的是CascadeView組件,所以有一部分的option必須變成公共的,在CascadeView組件也定義一次
* 外部通過CascadeView組件傳遞所有的option
* CascadeView內(nèi)部實(shí)例化CascadeItem的時(shí)候,再把PublicDefaults內(nèi)的option傳遞給CascadeItem
*/
var DEFAULTS = $.extend({}, PublicDefaults, {
prevItem: undefined, // 指向前一個(gè)級(jí)聯(lián)項(xiàng)
value: '' //初始時(shí)顯示的value
});
var CascadeItem = Class({
instanceMembers: {
init: function ($el, options) {
//通過this.base調(diào)用父類EventBase的init方法
this.base($el);
this.$el = $el;
this.options = this.getOptions(options);
this.prevItem = this.options.prevItem; //前一個(gè)級(jí)聯(lián)項(xiàng)
this.hasContent = false;//這個(gè)變量用來控制是否需要重新加載數(shù)據(jù)
this.cache = {};//用來緩存數(shù)據(jù)
var that = this;
//代理select元素的change事件
$el.on('change', function () {
that.trigger('changed.cascadeItem');
});
//當(dāng)前一個(gè)級(jí)聯(lián)項(xiàng)的值發(fā)生改變的時(shí)候,根據(jù)需要做清空和重新加載數(shù)據(jù)的處理
this.prevItem && this.prevItem.on('changed.cascadeItem', function () {
//只要前一個(gè)的值發(fā)生改變并且自身有內(nèi)容的時(shí)候,就得清空內(nèi)容
that.hasContent && that.clear();
//如果不是第一個(gè)級(jí)聯(lián)項(xiàng),同時(shí)前一個(gè)級(jí)聯(lián)項(xiàng)沒有選中有效的option時(shí),就不處理
if (that.prevItem && $.trim(that.prevItem.getValue()) == '') return;
that.load();
});
var value = $.trim(this.options.value);
value !== '' && this.one('render.cascadeItem', function () {
//設(shè)置初始值
that.$el.val(value.split(','));
//通知后面的級(jí)聯(lián)項(xiàng)做清空和重新加載數(shù)據(jù)的處理
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) {
//保留第一個(gè)option
$el.children().filter(':gt(0)').remove();
} else {
//清空全部
$el.html('');
}
//通知后面的級(jí)聯(lián)項(xiàng)做清空和重新加載數(shù)據(jù)的處理
this.trigger('changed.cascadeItem');
this.hasContent = false;//表示內(nèi)容為空
},
load: function () {
var opts = this.options,
paramValue,
that = this,
dataKey;
//dataKey是在cache緩存時(shí)用的鍵名
//由于第一個(gè)級(jí)聯(lián)項(xiàng)的數(shù)據(jù)是頂層數(shù)據(jù),所以在緩存的時(shí)候用的是固定且唯一的鍵:root
//其它級(jí)聯(lián)項(xiàng)的數(shù)據(jù)緩存時(shí)用的鍵名跟前一個(gè)選擇的option有關(guān)
if (!this.prevItem) {
paramValue = opts.defaultParam;
dataKey = 'root';
} else {
paramValue = this.prevItem.getParamValue();
dataKey = paramValue;
}
//先看數(shù)據(jù)緩存中有沒有加載過的數(shù)據(jù),有就直接顯示出來,避免Ajax
if (dataKey in this.cache) {
this.render(this.cache[dataKey]);
} else {
var params = {};
params[opts.paramName] = paramValue;
Ajax.get(opts.url, params).done(function (res) {
//resolveAjax這個(gè)回調(diào)用來在外部解析ajax返回的數(shù)據(jù)
//它需要返回一個(gè)data數(shù)組
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 value="',
item[opts.valueField],
'" data-param-value="',//將paramField對(duì)應(yīng)的值存放在option的data-param-value屬性上
item[opts.paramField],
'">',
item[opts.textField],
'</option>'].join(''));
});
//采用append的方式動(dòng)態(tài)添加,避免影響第一個(gè)option
//最后還要把value設(shè)置為空
this.$el.append(html.join('')).val('');
this.hasContent = true;//表示有內(nèi)容
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說明

演示代碼的結(jié)構(gòu):

其中框起來的就是演示的相關(guān)部分。html/regist.html是演示效果的頁面,js/app/regist.js是演示效果的入口js:

define(function (require, exports, module) {
  var $ = require('jquery');
  var CascadeView = require('mod/cascadeView');
  function publicSetCascadeView(fieldName, opts) {
    this.cascadeView = new CascadeView({
      $elements: $('#' + fieldName + '-view').find('select'),
      url: '../api/cascade.json',
      onChanged: this.onChanged,
      values: opts.values,
      keepFirstOption: this.keepFirstOption,
      resolveAjax: function (res) {
        if (res.code == 200) {
          return res.data;
        }
      }
    });
  }
  var LOCATION_VIEWS = {
    licenseLocation: {
      $input: $('input[name="licenseLocation"]'),
      keepFirstOption: true,
      setCascadeView: publicSetCascadeView,
      onChanged: function(e, value){
        LOCATION_VIEWS.licenseLocation.$input.val(value);
      }
    },
    companyLocation: {
      $input: $('input[name="companyLocation"]'),
      keepFirstOption: false,
      setCascadeView: 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()
  });
});

注意以上代碼中LOCATION_VIEWS這個(gè)變量的作用,因?yàn)轫撁嫔嫌卸鄠€(gè)級(jí)聯(lián)組件,這個(gè)變量其實(shí)是通過策略模式,把各個(gè)組件的相關(guān)的東西都用一種類似的方式管理起來而已。如果不這么做的話,很容易產(chǎn)生重復(fù)代碼;這種形式也比較有利于在入口文件這種處理業(yè)務(wù)邏輯的地方,進(jìn)行一些業(yè)務(wù)邏輯的分離與封裝。

5. others

這估計(jì)是在現(xiàn)在公司寫的最后一篇博客,過兩天就得去新單位去上班了,不確定還能否有這么多空余的時(shí)間來記錄平常的工作思路,但是好歹已經(jīng)培養(yǎng)了寫博客的習(xí)慣,將來沒時(shí)間也會(huì)擠出時(shí)間來的。今年的目標(biāo)主要是拓寬知識(shí)面,提高代碼質(zhì)量,后續(xù)的博客更多還是在組件化開發(fā)這個(gè)類別上,希望以后能夠得到大家的繼續(xù)關(guān)注腳本之家網(wǎng)站!

相關(guān)文章

最新評(píng)論