2015-10-18 1 views
0

При использовании свойства knockout.validation свойство isValid будет неверно сообщать о своем статусе, но только когда оно «внутри» ko.foreach.При использовании проверки нокаута isValid сообщения некорректно при привязке внутри ko.foreach

Используя фрагмент кода ниже, если вы запустите и введите следующие значения (д, г, 10) в том же текстовом поле, вы получите следующий результат:

  • self.InvoiceAmount является д и сам .InvoiceAmount.isValid верно
  • self.InvoiceAmount является г и self.InvoiceAmount.isValid ложно
  • self.InvoiceAmount 10 и self.InvoiceAmount.isValid ложна

Как вы можете видеть, v правило alidation для числа и q не должно сообщать об истинности, а также 10 не должны сообщать об ошибке.

Похоже, что исполнение выполнено, потому что InvoiceAmount.subcribe выполняется до InvoiceAmount.isValid.subscribe.

Когда вид модели просто простое свойство, результаты, как и ожидалось:

  • self.item является д и self.item.isValid ложно
  • self.item является г и я. item.isValid ложна
  • self.item 10 и self.item.isValid верно

Кто знает, почему это может происходить?


< Редактировать >

Я очень плохо задавая правильный вопрос, так что я постараюсь сделать его немного более ясно, что я после. К сожалению;)

мне нужно знать, если значение входного сигнала является действительным, если внутри функцией «подписаться» из наблюдаемых, который связан с этим входом.

Я использую значение isValid внутри функцию подписки, чтобы определить, может ли выполнение продолжить (isValid is true) или если выполнение должно прекратиться (isValid is false).

Когда вход связан с наблюдаемым внутри ko.foreach, значение isValid не обновляется до ПОСЛЕ выполняется функция подписки наблюдаемого. Это означает, что я получаю устаревшее значение для isValid, когда мне нужно решить, может ли выполнение продолжить или если выполнение должно прекратиться.

</Edit >


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>

+0

я вижу что-то странное с числовым bindingHandlers я пытался модифицировать с помощью 'autoNumeric', которые я знаю о его проверить здесь http://jsfiddle.net/LkqTU/27291/. вы можете направить свой ответ на автономию здесь http://stackoverflow.com/questions/27369871/knockoutjs-intercept-to-one-decimal-place/27372190#27372190 –

+0

Не глядя дальше, я предполагаю, что вы можете использовать '.extend ({notify: always})' KO extender. Это гарантирует, что ваши подписчики будут уведомлены каждый раз, когда доступ к значению будет достигнут, даже если он останется прежним. – Tyblitz

ответ

2

Полностью переработанный ответ:

Это, скорее всего, ошибка будет зависеть от порядка подразделам scriptions. Существуют состояния перехода для системы зависимостей, в течение которых общее состояние является непоследовательным. Система предназначена для достижения согласованного состояния, а не для каждого в одно мгновение.

Общим трюком, позволяющим системному времени достичь согласованного состояния, является обертка 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>

+0

Спасибо, что ответили! Мне было ужасно задавать этот вопрос, поэтому я снова обновляю свой пост. Мой журнал был попыткой показать, что внутри InvoiceAmount.subscribe isValid сообщает состояние для предыдущего значения InvoiceAmount. Кажется, что ваш код показывает правильный вывод в журнале, но за кулисами InvoiceAmount.subscribe все еще выполняется до isValid.subscribe, но пользовательский интерфейс обновляется слишком быстро, чтобы увидеть это изменение. Меня больше беспокоит порядок *, который они меняют, поведение подписки, когда внутри ko.foreach не то же самое, что и не в ko.foreach. – Mike

+0

@Mike Посмотрите, не поможет ли этот ответ вам лучше. –

+0

Этот ответ работает лучше для меня, большое вам спасибо! Еще один (глупый) вопрос относительно решения. Просто интересно, как время ожидания 0 будет достаточным для того, чтобы система могла перейти в это согласованное состояние? Еще раз спасибо! – Mike

Смежные вопросы