2012-11-18

ZK-1220 的故事

ZK-1220 (又)是一個抓蟲三刻鐘,除蟲三秒鐘的 issue。

故事是這樣的,在 ZK 6.0 之後 InputElement/InputWidget(Java/JS)增加了 instant 這個 attribute。 當 instant="true" 的時候,所有的改變會馬上送到 server 端。 如果 end-user 在有設定 instantTextbox 輸入之後馬上(更精準地說是 350ms 內)改變游標位置, 則游標還是會回到最尾端。

以我在 Window 7(64bit)上測試的結果,Chrome 23.x、Safari 5.1.7 會炸這個問題; Firefox 16 跟 Opera(忘記那一版,後來就更新了 XD)不會; 至於 IE 9 就真的神奇了,在 zksandbox 上測試會,獨立開 webapp 跑 ZUL 不會...... Orz。

在 source code 裡頭砍殺的路徑大概是

InputWidget.doKeyUp_():直接找 _instant 的結果 XD
    _startOnChanging()

在這裡發現把曲折離奇的這行拿掉、或是把 timeout 值設的很小,游標就不會亂跳了。

wgt._tidChg = setTimeout(
    wgt.proxy(_onChanging), zul.inp.InputWidget.onChangingDelay);

這裡稍微解釋一下為什麼要這麼曲折離奇(至於那個 widget.proxy 的功能就自己去看 API [逃])。 雖然說是 instant,不過真的要搞到一輸入就馬上辦, 隨便幾個人一直按著按鍵不放就自動形成 DOS 了。 所以在 _startOnChange() 一開始就先呼叫 _stopOnChanging() 清掉之前設定的 setTimeout(), 然後再重新註冊一次 setTimeout()-- 也就是嚴格來說是 keyin 告一段落才送資料給 server。

回到 bug 上,第一直覺是有那一行程式重新 assign 一次 value 值, 然後首先懷疑是 server side 重新 assign。 結果就鬧了一個笑話:

奇怪,我設了 instant="true" 為什麼 Tabbox.service() 始終沒呼叫到?

廢話...... 你又沒註冊 onChange,資料幹麼送回 server 端?

至於那個 onChanging 不管怎麼看都跟 instant="true" + onChange="foo()" 意思相同? 那麼還有其他 event 可以搭配 instant 嗎? 如果沒有那 client 端的 code 應該直接把兩個綁在一起判斷以增加效率?

扯遠了,上面這段跟 bug 無關 XD

因為沒掛 onChange 也依然會炸,所以兇手一定就在 client side 的程式碼當中。 接著因為腦殘懷疑到 validate 上、又眼殘的關係又繞了一大圈, 才發現是 _onChange() 裡頭呼叫的 InputWidget.updateChange_() 的這行出問題:

inp.value = value = this.coerceToString_(vi.value);

inp 到底是啥不重要,反正就是輸入框 XD。 之前不知道為什麼完全忽略掉他,只注意到後頭的 coerceToString_(), 我只能說這種一路「等」到天涯海角的寫法真是令人想要直接跳過去 [炸]。 至於coerceToString_()的功用是要把輸入的值改成預期的格式, 這在FormatInputElement/FormatWidget(Java/JS) 的 subclass 會用到; 但是為什麼卡在這裡作而不是放到FormatWidget` 裡頭去呢? [孟獲孟獲孟獲]

好的,解法就是先檢查 inp.value 是不是等於 coerceToString_() 後的 value, 如果不是的話再 assing 進去。 至於像 DateboxTimebox 還是有指定 format 的 Intbox 就無法避免這個問題 ,畢竟他們本來就試著要改變輸入框的值--當然,在這種有 format 的 input widget 上用 instant 也絕對不是啥好想法、太不 user friendly 了。

報告完畢。

No comments:

Post a Comment