2014-01-23 144 views
11

我想使用此技術:make an input only-numeric type on knockout淘汰賽自定義數字綁定

允許用戶只輸入數字。

然而,該技術不更新UI觀察到的值。

HTML:

<span data-bind="text: Interval" ></span> 
<input data-bind="numeric: Interval" /> 

綁定:

ko.bindingHandlers.numeric = { 
    init: function (element, valueAccessor) { 
     $(element).on("keydown", function (event) { 
      // Allow: backspace, delete, tab, escape, and enter 
      if (event.keyCode == 46 || event.keyCode == 8 || event.keyCode == 9 || event.keyCode == 27 || event.keyCode == 13 || 
       // Allow: Ctrl+A 
       (event.keyCode == 65 && event.ctrlKey === true) || 
       // Allow: . , 
       (event.keyCode == 188 || event.keyCode == 190 || event.keyCode == 110) || 
       // Allow: home, end, left, right 
       (event.keyCode >= 35 && event.keyCode <= 39)) { 
       // let it happen, don't do anything 
       return; 
      } 
      else { 
       // Ensure that it is a number and stop the keypress 
       if (event.shiftKey || (event.keyCode < 48 || event.keyCode > 57) && (event.keyCode < 96 || event.keyCode > 105)) { 
        event.preventDefault(); 
       } 
      } 
     }); 
    }  
}; 

因此,結合不允許進入數字以外的字符,但是當焦點在input丟失,相應的觀察到的沒有更新(所以span元素沒有改變)。

注:

我不需要讓用戶輸入的非數字字符轉換成輸入。我知道還有其他的解決方案,像ko數字擴展,可以將所有東西都轉換成數字,但我不需要它。我需要一個允許輸入數字的解決方案(包括退格等)。

+1

你可以創建一個工作小提琴顯示問題? –

+0

你忘了包括 - 對於負數 –

回答

1

確實,這不會更新您的可觀察值。自定義綁定不完整。在我看來,這只是一個想法的例子,而不是一個可行的解決方案。

但是,在您鏈接的問題,實際上是在評論一個更好的辦法的地方。它是使用Knockout擴展器。請參閱實時示例1 http://knockoutjs.com/documentation/extenders.html

有幾個原因是更好的: 1.更強大。例如,您仍然可以在剪貼板中粘貼一串文本到您的解決方案中。 2.更方便用戶使用。您的解決方案明顯禁用了一堆密鑰。這根本不是用戶友好的。 Knockout提出的解決方案確保最終的價值是正確的。 3.更好的代碼分離和可維護性:您的HTML可以包含普通的ol值綁定。一旦需求增加,值應該是數值,那麼只需在視圖模型中擴展觀察值。您所做的唯一更改就是在JavaScript中,因爲它是功能性的,而不是演示文稿。這種變化本身也是存在的,它很清楚擴展器對於任何可能在計算或w/e中使用可觀測數據的人所做的事情。

+0

我已經看過這個例子,但在我的情況下,我真的不需要允許輸入其他值只是數字。所以,這樣的擴展器不適合我的需求。 – renathy

6

爲數字僅包含數字的固體路線將用戶增量。

我們沒有追蹤按鍵。只需訂閱觀察值就可以更容易地截取值。然後,我們可以做一些正則表達式,使我們能夠評估輸入是否是數字。如果輸入不是數字,我們將刪除非數字字符。因此不允許非數字輸入。

FIDDLE

HTML

<input type="text" data-bind="value: myNum, valueUpdate: 'afterkeyup'" /> 

JS

(function(ko) { 

    ko.observable.fn.numeric = function() { 
     // the observable we are extending 
     var target = this; 

     // subscribe to the observable so we can 
     // intercept the value and do our custom 
     // processing. 
     this.subscribe(function() { 
      var value = target(); 
      // this will strip out any non numeric characters 
      target(value.replace(/[^0-9]+/g,'')); //[^0-9\.]/g - allows decimals 
     }, this); 

     return target; 
    }; 

    function ViewModel() { 
     this.myNum = ko.observable().numeric(); 
    }; 

    ko.applyBindings(new ViewModel()); 

})(ko); 
+0

喜歡這種方法。好的解決方案 – pimbrouwers

0

您可以提高結合的處理程序,以支持valueAccessor

的修改

綁定:

ko.bindingHandlers.numeric = { 
    init: function (element, valueAccessor) { 
     var value = valueAccessor(); 
     $(element).on("keydown", function (event) { 
      // Allow: backspace, delete, tab, escape, and enter 
      if (event.keyCode == 46 || event.keyCode == 8 || event.keyCode == 9 || event.keyCode == 27 || event.keyCode == 13 || 
       // Allow: Ctrl+A 
       (event.keyCode == 65 && event.ctrlKey === true) || 
       // Allow: . , 
       (event.keyCode == 188 || event.keyCode == 190 || event.keyCode == 110) || 
       // Allow: home, end, left, right 
       (event.keyCode >= 35 && event.keyCode <= 39)) { 
       // let it happen, don't do anything 
       return; 
      } 
      else { 
       // Ensure that it is a number and stop the keypress 
       if (event.shiftKey || (event.keyCode < 48 || event.keyCode > 57) && (event.keyCode < 96 || event.keyCode > 105)) { 
        event.preventDefault(); 
       } 
      } 
     }); 

     $(element).change(function() { 
      value($(element).val()); 
     }); 
    }  
}; 

在這種情況下, HTML將爲

<span data-bind="text: Interval" ></span> 
<input data-bind="numeric: Interval" /> 

FIDDLE

1

這是我的固定版本,考慮到上述所有,但作爲實際價值綁定支持作爲源/目標的不可觀察對象

編輯:淘汰賽的精縮版不公開writeValueToProperty功能和twoWayBindings。所以我們應該克隆writeValueToProperty並使用_twoWayBindings。 我更新了代碼以支持縮小版本的挖空。

ko.expressionRewriting._twoWayBindings.numericValue = true; 
ko.expressionRewriting.writeValueToProperty = function (property, allBindings, key, value, checkIfDifferent) { 
    if (!property || !ko.isObservable(property)) { 
     var propWriters = allBindings.get('_ko_property_writers'); 
     if (propWriters && propWriters[key]) 
      propWriters[key](value); 
    } else if (ko.isWriteableObservable(property) && (!checkIfDifferent || property.peek() !== value)) { 
     property(value); 
    } 
}; 
ko.bindingHandlers.numericValue = { 
    init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { 
     $(element).on("keydown", function (event) { 
      // Allow: backspace, delete, tab, escape, and enter. 
      if (event.keyCode == 46 || event.keyCode == 8 || event.keyCode == 9 || event.keyCode == 27 || event.keyCode == 13 || 
       // Allow: Ctrl+A 
       (event.keyCode == 65 && event.ctrlKey === true) || 
       // Allow: . , 
       (event.keyCode == 188 || event.keyCode == 190 || event.keyCode == 110) || 
       // Allow: home, end, left, right. 
       (event.keyCode >= 35 && event.keyCode <= 39)) { 
       // Let it happen, don't do anything. 
       return; 
      } 
      else { 
       if (event.shiftKey || (event.keyCode < 48 || event.keyCode > 57) && (event.keyCode < 96 || event.keyCode > 105)) { 
        event.preventDefault(); 
       } 
      } 
     }); 

     var underlying = valueAccessor(); 
     var interceptor = ko.dependentObservable({ 
      read: function() { 
       if (ko.isObservable(underlying) == false) { 
        return underlying; 
       } else { 
        return underlying(); 
       } 
      }, 
      write: function (value) { 
       if (ko.isObservable(underlying) == false) { 
        if (!isNaN(value)) { 
         var parsed = parseFloat(value); 
         ko.expressionRewriting.writeValueToProperty(underlying, allBindingsAccessor, 'numericValue', !isNaN(parsed) ? parsed : null); 
        } 
       } else { 
        if (!isNaN(value)) { 
         var parsed = parseFloat(value); 
         underlying(!isNaN(parsed) ? parsed : null); 
        } 
       } 
      } 
     }); 
     ko.bindingHandlers.value.init(element, function() { return interceptor; }, allBindingsAccessor, viewModel, bindingContext); 
    }, 
    update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { 
     ko.bindingHandlers.value.update(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext); 
    } 
}