从所有可观察值自动修剪空白

时间:2012-05-24 21:23:02

标签: knockout.js

我在Knockout中有一个ViewModel,它主要来自映射插件(即动态)。这很好用。但是,现在我的客户希望我确保在提交到服务器之前所有输入都有空白区域。显然,修剪代码非常简单,但对于Knockout来说相对较新,我不确定该代码的确切位置。我读到了extenders,但这看起来非常冗长和重复,回过头来添加到每个observable。另外,我甚至不确定我是否能够动态生成observable(la,即映射插件)。

我是否可以扩展/覆盖任何中心机制,每次可观察到的变化时我都可以注入一些修剪代码?基本上我正在努力避免花费大量时间浏览所有表单并添加如果我不需要HTML中的特殊绑定语法。

感谢。

6 个答案:

答案 0 :(得分:19)

我遇到了同样的问题。我写了一个扩展程序,因此您可以在视图模型中调用trimmed,而无需更改绑定。例如:

var vm = {
    myValue: ko.observable('').trimmed()
}

扩展名:

ko.subscribable.fn.trimmed = function() {
    return ko.computed({
        read: function() {
            return this().trim();
        },
        write: function(value) {
            this(value.trim());
            this.valueHasMutated();
        },
        owner: this
    });
};

代码为on JSFiddle,附带示例。

答案 1 :(得分:9)

万一有人遇到Knockout较新版本的问题,目前排名靠前的答案将无法正常运作。

这是一个更新的fiddle和代码,用于显示所需的更改:

ko.subscribable.fn.trimmed = function() {
    return ko.computed({
       read: function() {
           return this().trim();
       },
       write: function(value) {
           this(value.trim());
           this.valueHasMutated();
       },
       owner: this
   }).extend({ notify: 'always' });
};

如果有人知道为什么现在需要extend,请告诉我。我花了很长时间才弄清楚为什么它在Knockout 3.1.0中无法正常工作

答案 2 :(得分:4)

您可以编写一个自定义绑定来修剪observable。与此相似的东西

http://jsfiddle.net/belthasar/fRjdq/

答案 3 :(得分:3)

使用Joe的解决方案作为起点,我们实现的方式略有不同。

<强>注意:

  • ko.observable()括号中没有任何内容
  • 新的trimmed读取函数只返回this(),并且不会获得任何null或未定义的异常。

型号代码:

var vm = {
    myValue: ko.observable().trimmed()
}

扩展程序:

ko.subscribable.fn.trimmed = function() {
    return ko.computed({
        read: function() {
            return this();
        },
        write: function(value) {
            this(value.trim());
            this.valueHasMutated();
        },
        owner: this
    });
};

答案 4 :(得分:3)

您可以创建一个在内部调用value绑定的自定义绑定,也可以在value绑定实际绑定之前覆盖自动修剪(不推荐)。

基本理念:

  • 拦截value绑定
  • 将传递的observable包裹在computed
  • 从计算中生成绑定readwrite而不是从原始可观察
  • 当新输入到达时,在我们编写之前修剪它
  • 当模型值发生变化时,修剪它并更新模型和模型。如果需要,可以使用UI

ko.bindingHandlers.trimmedValue = {
  init: function(element, valueAccessor, allBindings) {
    const ogValue = valueAccessor();
    let newVa = valueAccessor;
    
    // If this is a type="text" element and the data-bound value is observable,
    // we create a new value accessor that returns an in-between layer to do
    // our trimming
    if (element.type === "text" && ko.isObservable(ogValue)) {
      const trimmedValue = ko.observable().extend({"trim": true});
      
      // Write to the model whenever we change
      trimmedValue.subscribe(ogValue);
      
      // Update when the model changes
      ogValue.subscribe(trimmedValue);
      
      // Initialize with model value
      trimmedValue(ogValue());
      
      // From now on, work with the trimmedValue 
      newVa = () => trimmedValue;
    }

    // Note: you can also use `ko.applyBindingsToNode`
    return ko.bindingHandlers.value.init(element, newVa, allBindings)
  }
}

// Our observable to check our results with
var myObs = ko.observable("test ");
myObs.subscribe(function(newValue) {
  console.log("Change: \"" + newValue + "\"");
});

// The extender that does the actual trim
ko.extenders.trim = function(target, option) {
  return ko.computed({
    read: target,
    write: function(val) {
      target(
        val && typeof val.trim === "function"
          ? val.trim()
          : val
      );

      // This makes sure the trimming always resets the input UI
      if (val !== target.peek()) {
        target.valueHasMutated();
      }
    }
  }).extend({notify: "always"});
};

ko.applyBindings({
  myObs: myObs
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

<h4><code>type="text" trimmedValue</code></h4>
<input type="text" data-bind="trimmedValue: myObs">

如果您不关心模型中某些不需要的valueHasMutated

棘手的部分是确定您希望在模型中接收哪些更新...下面的示例不会触发valueHasMutated,也不会改变模型的可观察性。 但是,如果将模型值更改为未修剪的字符串,绑定处理程序将立即重置它。例如:myObs(" test ")将触发

  1. Change: " test "
  2. Change: "test"
  3. 如果您只需要从UI修剪到模型,并且不介意一些额外的更新,您可以使用:

    ko.bindingHandlers.value.init = function(element, valueAccessor, allBindings) {
      const ogValue = valueAccessor();
      const newVa = (element.type === "text" && ko.isObservable(ogValue))
        ? () => ogValue.extend({"trim": true})
        : valueAccessor;
    
      return ogValueInit(element, newVa, allBindings)
    };
    

    覆盖默认的value绑定

    要将此行为用作标准行为(同样不推荐),您可以执行以下操作:

    const ogValueInit = ko.bindingHandlers.value.init;
    ko.bindingHandlers.value.init = function( /*... */ ) {
      // ...
      return ogValueInit( /* ... */);
    };
    

    const ogValueInit = ko.bindingHandlers.value.init;
    ko.bindingHandlers.value.init = function(element, valueAccessor, allBindings) {
      const ogValue = valueAccessor();
      let newVa = valueAccessor;
    
      // If this is a type="text" element and the data-bound value is observable,
      // we create a new value accessor that returns an in-between layer to do
      // our trimming
      if (element.type === "text" && ko.isObservable(ogValue)) {
        const trimmedValue = ko.observable().extend({"trim": true});
    
        // Write to the model whenever we change
        trimmedValue.subscribe(ogValue);
    
        // Update when the model changes
        ogValue.subscribe(trimmedValue);
    
        // Initialize with model value
        trimmedValue(ogValue());
    
        // From now on, work with the trimmedValue 
        newVa = () => trimmedValue;
      }
    
      return ogValueInit(element, newVa, allBindings)
    };
    
    // Our observable to check our results with
    var myObs = ko.observable("test ");
    myObs.subscribe(function(newValue) {
      console.log("Change: \"" + newValue + "\"");
    });
    
    // The extender that does the actual trim
    ko.extenders.trim = function(target, option) {
      return ko.computed({
        read: target,
        write: function(val) {
          target(
            val && typeof val.trim === "function"
              ? val.trim()
              : val
          );
    
          // This makes sure the trimming always resets the input UI
          if (val !== target.peek()) {
            target.valueHasMutated();
          }
        }
      }).extend({notify: "always"});
    };
    
    ko.applyBindings({
      myObs: myObs
    });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
    
    <h4><code>type="text" value</code></h4>
    <input type="text" data-bind="value: myObs">

答案 5 :(得分:0)

一种对我们有用的替代方法-在编辑字段时进行修剪:

$(document.body).on('blur', 'input, textarea', function () { this.value = this.value.trim(); $(this).trigger('change'); });

“更改”事件的触发器确保KO接受了更改(已通过KO v2测试)。