简体   繁体   English

自定义Knockout绑定无法正常工作

[英]Custom Knockout binding not working

I am currently using an event binding to format telephone numbers (into xxx-xxx-xxxx format) and I want to create a reusable custom binding to this for future use in our app. 我目前正在使用事件绑定来格式化电话号码(到xxx-xxx-xxxx格式),我想创建一个可重复使用的自定义绑定,以便将来在我们的应用程序中使用。 The current event binding works perfectly but I cannot get the custom binding to work correctly. 当前事件绑定工作正常,但我无法使自定义绑定正常工作。 Can anyone take a look below and tell me my issue? 任何人都可以看看下面,告诉我我的问题?

Current event binding with viewModel method: 使用viewModel方法绑定当前事件:

<input class="form-control" id="Phone"  type="text" 
       data-bind="event: {blur: formatPhone}, enable: isInputMode, value: Phone" />

self.Phone = ko.observable(model.MainPhone).extend({ maxLength: 20 });

self.formatMainPhone = function() {
        var tempString = self.Phone().replace(/\D+/g, "").replace(/^[01]/, "").replace(/(\d{3})(\d{3})(\d{4})/, "$1-$2-$3").substring(0, 12);
        self.Phone(tempString);
    }

Custom binding handler that does not work: 自定义绑定处理程序不起作用:

<input class="form-control max225" id="Phone" type="text" 
           data-bind="formatPhoneNumber: Phone, enable: isInputMode, value: Phone" />

self.Phone = ko.observable(model.MainPhone).extend({ maxLength: 20 });

ko.bindingHandlers.formatPhoneNumber = {
        update: function (element, valueAccessor) {            
            var phone = ko.utils.unwrapObservable(valueAccessor());
            var formatPhone = function () {
                return phone.replace(/\D+/g, "").replace(/^[01]/, "").replace(/(\d{3})(\d{3})(\d{4})/, "$1-$2-$3").substring(0, 11);
            }
            ko.bindingHandlers.value.update(element, formatPhone);
        }
    };

Your binding is attempting to hijack the update of the default "value" binding, which looking at the knockout source code, appears to have been deprecated. 您的绑定试图劫持默认“值”绑定的更新,该绑定查看了淘汰源代码,似乎已被弃用。

'update': function() {} // Keep for backwards compatibility with code that may have wrapped value binding

You'll have to change your binding so that it uses init instead. 您必须更改绑定,以便它使用init。

ko.bindingHandlers.value.init(element, formatPhone, allBindings);

EDIT: 编辑:

This might be closer to what you want. 这可能更接近你想要的。 Instead of using the update binding this creates an intermediary computed observable and then uses value.init to bind the textbox to that. 而不是使用更新绑定,这将创建一个中间计算observable,然后使用value.init将文本框绑定到该。 We never need the update binding because the computed will take care of propagating changes for you. 我们永远不需要更新绑定,因为计算将负责为您传播更改。

ko.bindingHandlers.formatPhoneNumber = {
    init: function (element, valueAccessor, allBindings) {            
      var source = valueAccessor();      
      var formatter = function(){
        return ko.computed({
          read: function(){ return source(); },
          write: function(newValue){
            source(newValue.replace(/\D+/g, "").replace(/^[01]/, "").replace(/(\d{3})(\d{3})(\d{4})/, "$1-$2-$3").substring(0, 12));
          }
        })
      };

      ko.bindingHandlers.value.init(element, formatter, allBindings);
    }
  };

EDIT 2 - more explanation 编辑2 - 更多解释

Using the update binding for formatPhoneNumber tells knockout to run that code any time the value changes. 使用formatPhoneNumber的更新绑定告诉knockout在值发生变化时运行该代码。 That sounds good in theory, but lets break it down. 这在理论上听起来不错,但让我们把它分解。

  1. Unwraps the accessor and gets the flat value. 打开访问器并获得平坦值。
  2. Creates a format function that returns a formatted value. 创建一个返回格式化值的格式函数。
  3. piggyback's the value binding using the format function. 搭载使用格式函数的值绑定。

Because this is an update binding unwrapping the accessor in step 1 creates a trigger to re-evaluate the binding whenever the accessor value changes. 因为这是一个更新绑定,所以在步骤1中展开访问者会创建一个触发器,以便在访问者值发生更改时重新评估绑定。 Then in step 3 you're telling knockout to re-execute the value.update binding which currently is just an empty function and does nothing. 然后在第3步中,你告诉knockout重新执行value.update绑定,它当前只是一个空函数,什么都不做。 If you change that to use value.init instead things might actually function for formatting the output, but you're telling knockout to re-initialize the init binding every time the value changes. 如果你改变它来使用value.init而事实上可能实际上用于格式化输出,但是你告诉knockout每次值改变时重新初始化init绑定。

update: function(element, valueAccessor, allBindings) {
    var phone = ko.utils.unwrapObservable(valueAccessor());
    var formatPhone = function() { return phone.replace(...)}
    ko.bindingHandlers.value.init(element, formatPhone, allBindings);
}

The binding is getting re-created and passed new initial values. 重新创建绑定并传递新的初始值。 This also means that it's only a one-way binding because changes to the front-end can't make it back to your model to update the backing observable. 这也意味着它只是一个单向绑定,因为对前端的更改不能使其返回到模型以更新后备可观察对象。

Now if you change your own binding to an init binding, and from there call the value.init binding as well it will only get initialized the one time, but the next problem is that the function you're binding to isn't going to know when to update. 现在,如果您将自己的绑定更改为init绑定,并从那里调用value.init绑定,它只会初始化一次,但下一个问题是您绑定的函数不会知道什么时候更新。

init: function(element, valueAccessor, allBindings) {
    var phone = ko.utils.unwrapObservable(valueAccessor());
    var formatPhone = function() { return phone.replace(...)}
    ko.bindingHandlers.value.init(element, formatPhone, allBindings);
}

Since it's just a normal js function which is being passed an already-unwrapped flat value it will always always give the same result based on that original value of phone. 因为它只是一个正常的js函数,它正在传递一个已经解开的平坦值,它总是会根据手机的原始值给出相同的结果。 Passing the value.init binding a computed observable instead ensures that updates to the accessor observable trigger the format function to update from within the existing binding. 传递value.init绑定计算的observable,确保对accessor observable的更新触发format函数从现有绑定中更新。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM