【问题标题】:Automatically trim whitespace from all observable values自动修剪所有可观察值的空白
【发布时间】:2012-05-31 12:13:06
【问题描述】:

我在 Knockout 中有一个 ViewModel,它主要来自映射插件(即动态)。这工作正常。但是,现在我的客户希望我确保所有输入在提交到服务器之前都已删除空格。显然,修剪代码非常简单,但是对于 Knockout 来说相对较新,我不确定该代码到底应该放在哪里。我读到了extenders,但这似乎非常冗长和重复,要返回并将其添加到每个可观察对象中。另外,我什至不确定我是否可以对动态生成的 observables 执行此操作(例如,映射插件)。

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

谢谢。

【问题讨论】:

  • 您提到它们只需要在提交之前进行修剪。使用 ko.mapping.toJS(model) 取消映射你的 observables,然后编写一个帮助器来递归每个属性并修剪它。
  • 嗯。这还算公平。也许我会试试。虽然我希望有一种方法可以在每次可观察更新时运行一些代码。
  • FWIW,因为我在输入更改时运行验证器,所以我只是在开始时运行修剪功能。
  • 很多好的答案,但如果不仔细阅读代码并更新每个页面上的每个 observable,仍然没有任何方法可以全面发挥作用。您是否找到了自动执行此操作的方法?

标签: knockout.js


【解决方案1】:

我遇到了同样的问题。我写了一个扩展,这样你就可以在你的视图模型中调用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,带有示例。

【讨论】:

  • 这在功能上是否与将其添加到 ko.extenders 命名空间相同?
  • @MatthewNichols 是的。 ko.extenders 实际上是用 ko.subscribable 实现的。不同之处在于,您需要与 ko.extenders 争论,例如.extend({trimmed: true}).extend({trimmed: null})。通过让您直接从 observable 调用函数,这种方式也更简洁。见freshbrewedcode.com/joshbush/2011/12/27/…
  • 这只适用于支持.trim()的浏览器,对吗? (即不支持 IE7 或 IE8)
  • @johnthexiii 正确。选项:使用jQuery.trim(s)s.replace(/^\s+|\s+$/g, '') 代替s.trim()。或者,您可以添加自己的 polyfill 以使其支持这些浏览器。见stackoverflow.com/questions/498970/…
  • @johnthexiii 刚刚向 JSFiddle 代码添加了旧版浏览器支持。
【解决方案2】:

以防万一有人在使用较新版本的 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 中不能正常工作

【讨论】:

【解决方案3】:

你可以编写一个自定义绑定来修剪 observable。类似的东西

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

【讨论】:

  • 虽然这可能有效,但我正在寻找一种不需要我编辑 HTML 中的所有绑定的解决方案..
  • 您能发布您的映射代码吗?你在使用 mappingOptions 吗?
  • 我的映射代码只是一个ajax调用的.done()函数中的ko.mapping.fromJS(data, {}, viewmodel)
  • 您可以从文档中添加映射选项。您可能可以从那里访问数据。自从我弄乱映射选项以来已经有一段时间了。 var mapping = { 'children': { key: function(data) { return ko.utils.unwrapObservable(data.id); } } } var viewModel = ko.mapping.fromJS(data, mapping);
  • 映射选项看起来很有趣,尤其是update 选项。但是,看起来我必须手动为每个 observable 创建一个更新选项。我可以在映射后应用这些选项吗?如果是这样,我可以映射、循环并应用选项,然后重新映射,但我不知道这是否可能?
【解决方案4】:

以 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
    });
};

【讨论】:

    【解决方案5】:

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

    基本思路:

    • 拦截value绑定
    • 将传递的 observable 包装在 computed
    • 从计算的而不是从原始的 observable 绑定 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">

    如果您不关心模型中一些不需要的valueHasMutateds

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

    1. Change: " test ",和
    2. Change: "test"

    如果您只需要从 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">

    【讨论】:

    • 谢谢。这看起来很有希望,但是阻止我在文本字段中输入任何空格。只要我按下空格键,它就会在我完成输入之前立即修剪空格
    • 您是否使用textInput 绑定而不是value?它修剪change 事件,该事件仅在您从input 移除焦点时才会发生。如果你想要实时更新,你需要一些额外的代码......
    【解决方案6】:

    对我们来说效果很好的另一种方法 - 在编辑字段时修剪:

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

    “更改”事件的触发器确保 KO 获取更改(使用 KO v2 测试)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-07-14
      • 2020-06-10
      • 1970-01-01
      • 1970-01-01
      • 2018-08-26
      • 2017-03-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多