使用敲除验证时,isValid在绑定到ko.foreach

时间:2015-10-18 04:10:13

标签: javascript validation knockout.js

使用knockout.validation时,isValid属性会错误地报告其状态,但只有当它在ko.foreach“内部”时才会报告。

使用下面的代码段,如果您在同一文本框中运行并输入以下值(q,z,10),您将获得以下输出:

  • self.InvoiceAmount是q和self.InvoiceAmount.isValid为true
  • self.InvoiceAmount是z和self.InvoiceAmount.isValid是false
  • self.InvoiceAmount为10且self.InvoiceAmount.isValid为false

如您所见,验证规则适用于数字,q不应报告为真,10不应报告错误。

执行顺序似乎搞砸了,因为InvoiceAmount.subcribe是在InvoiceAmount.isValid.subscribe之前执行的。

当视图模型只是一个简单属性时,结果如预期:

  • self.item为q且self.item.isValid为false
  • self.item为z,self.item.isValid为false
  • self.item为10且self.item.isValid为true

任何人都知道为什么会发生这种情况?

<小时/> 的&LT;编辑&gt;

我真的很难问正确的问题,所以我会试着让我更清楚一下我所追求的是什么。对不起;)

里面绑定到该输入的observable的“subscribe”函数时,我需要知道输入的值是否有效。

我在订阅函数中使用isValid值 来确定执行是否可以继续(isValid为true)或者执行是否必须停止(isValid为false)。

当输入绑定到ko.foreach中的observable时,isValid的值不会更新,直到 AFTER 执行observable的subscribe函数。这意味着当我需要确定执行是否可以继续或者执行是否必须停止时,我将获得isValid的陈旧值。

&LT; /编辑&gt;

ko.bindingHandlers.numeral = {
            init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
                ko.bindingHandlers.value.init(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext);
            },
            update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
                var value = ko.utils.unwrapObservable(valueAccessor()),
                    format = ko.utils.unwrapObservable(allBindingsAccessor().format) || ko.bindingHandlers.numeral.defaultFormat,
                    formattedValue = Number(value) ? numeral(value).format(format) : value;
                $(element).val(formattedValue);
            },
            defaultFormat: "0,0.00"
        };

        var viewModel = function(param) {

            var self = this;
          
            self.item = ko.observable().extend({ number: true });
            self.item.subscribe(function(value) {
                $("#log").append("<p>self.item is " + value + " and self.item.isValid is " + self.item.isValid() + "</p>");
            });

            self.items = ko.mapping.fromJS([]);

            ko.mapping.fromJS(param, {
                key: function(data) {
                    return data.CustomerId;
                },
                create: function(options) {
                    return new itemViewModel(options.data);
                }
            }, self.items);

        };

        var itemViewModel = function(item) {

            var self = this;

            ko.mapping.fromJS(item, {}, self);

            self.InvoiceAmount.extend({ number: true });

            self.InvoiceAmount.subscribe(function(value) {
                $("#log").append("<p>self.InvoiceAmount is " + value + " and self.InvoiceAmount.isValid is " + self.InvoiceAmount.isValid() + "</p>");
            });
        }

        $(function() {
            var vm = new viewModel([{ "InvoiceAmount": 10 }, { "InvoiceAmount": 20 }, { "InvoiceAmount": 30 }, { "InvoiceAmount": 40 }]);
            ko.applyBindings(vm);
        });
.error {
  color: #FF0000;
}
p {
  margin: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/numeral.js/1.5.3/numeral.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout-validation/2.0.3/knockout.validation.js"></script>

<div>
  single item:<br/>
<input type="text" data-bind="numeral: item"/>
  </div>

<div>
  foreach:<br/>
<table>
    <tbody data-bind="foreach: items">
    <tr>
        <td><input type="text" data-bind="numeral: InvoiceAmount"/></td>
    </tr>
    </tbody>
</table>
</div>

<div id="log"></div>

1 个答案:

答案 0 :(得分:2)

完全修订回复:

依赖订阅顺序很可能是错误的。存在依赖系统的转换状态,在此期间整体状态不一致。该系统旨在达到一致的状态,而不是在每个瞬间都处于一个状态。

让系统有时间达到一致状态的常用技巧是将setTimeout包裹在代码中,否则可能会在更改中执行。我已经修改了下面的代码来做到这一点,你可以看到报告的状态是正确的。

ko.bindingHandlers.numeral = {
  init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
    ko.bindingHandlers.value.init(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext);
  },
  update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
    var value = ko.utils.unwrapObservable(valueAccessor()),
      format = ko.utils.unwrapObservable(allBindingsAccessor().format) || ko.bindingHandlers.numeral.defaultFormat,
      formattedValue = Number(value) ? numeral(value).format(format) : value;
    $(element).val(formattedValue);
  },
  defaultFormat: "0,0.00"
};

var viewModel = function(param) {

  var self = this;

  self.item = ko.observable().extend({
    number: true
  });
  self.item.subscribe(function(value) {
    $("#log").append("<p>self.item is " + value + " and self.item.isValid is " + self.item.isValid() + "</p>");
  });

  self.items = ko.mapping.fromJS([]);

  ko.mapping.fromJS(param, {
    key: function(data) {
      return data.CustomerId;
    },
    create: function(options) {
      return new itemViewModel(options.data);
    }
  }, self.items);

};

var itemViewModel = function(item) {

  var self = this;

  ko.mapping.fromJS(item, {}, self);

  self.InvoiceAmount.extend({
    number: true
  });

  self.InvoiceAmount.subscribe(function(value) {
    setTimeout(function () {
      $("#log").append("<p>self.InvoiceAmount is " + value + " and self.InvoiceAmount.isValid is " + self.InvoiceAmount.isValid() + "</p>");
      }, 0);
  });
}

$(function() {
  var vm = new viewModel([{
    "InvoiceAmount": 10
  }, {
    "InvoiceAmount": 20
  }, {
    "InvoiceAmount": 30
  }, {
    "InvoiceAmount": 40
  }]);
  ko.applyBindings(vm);
});
.error {
  color: #FF0000;
}
p {
  margin: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/numeral.js/1.5.3/numeral.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout-validation/2.0.3/knockout.validation.js"></script>

<div>
  single item:
  <br/>
  <input type="text" data-bind="numeral: item" />
</div>

<div>
  foreach:
  <br/>
  <table>
    <tbody data-bind="foreach: items">
      <tr>
        <td>
          <input type="text" data-bind="numeral: InvoiceAmount" />
        </td>
      </tr>
    </tbody>
  </table>
</div>

<div id="log"></div>