18

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

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

谢谢。

4

6 回答 6

20

我有同样的问题。我写了一个扩展,这样你就可以调用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
    });
};

代码在 JSFiddle 上,带有示例。

于 2012-11-08T18:23:00.003 回答
10

以防万一有人在使用更新版本的 Knockout 时遇到此问题,当前排名靠前的答案将无法正常工作。

这是一个更新的小提琴和代码,以显示所需的更改:

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 中不能正常工作

于 2014-08-22T20:21:56.100 回答
4

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

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

于 2012-05-24T21:30:28.453 回答
3

以 Joe 的解决方案为起点,我们实现它的方式稍有不同。

注意:

  • ko.observable()括号中没有任何内容
  • 新的trimmedread 函数只是简单地返回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
    });
};
于 2014-07-01T17:23:36.890 回答
3

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

基本思想:

  • 拦截value绑定
  • 将传递的 observable 包装在一个computed
  • 进行绑定readwrite从计算的而不是从原始的 observable
  • 当新输入到达时,在我们写之前修剪它
  • 当模型值发生变化时,修剪它并根据需要更新模型和 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模型中一些不需要的 s

棘手的部分是确定您希望在模型中接收哪些更新......下面的示例不会触发valueHasMutated或改变模型的 observable。但是,如果您将模型值更改为未修剪的字符串,绑定处理程序将立即重置它。eg: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">

于 2017-07-27T16:22:25.190 回答
0

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

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

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

于 2019-05-03T23:02:54.010 回答