Breezeメタデータ(エンティティフレームワークのデータ属性から取得)をノックアウト検証拡張機能(knockout.validationを使用)に変換するユーティリティを作成した人はいますか?
6 に答える
エンティティからメタデータを読み取り、検証ルールを追加する関数を作成しました。
app.domain.indicador = (function () {
"use strict";
var constructor = function () {...}
var initializer = function indicadorInitializer(entity) {
var entityType = entity.entityType;
if (entityType) {
console.log(entityType);
for (var i = 0; i < entityType.dataProperties.length; i++) {
var property = entityType.dataProperties[i];
console.log(property);
var propertyName = property.name;
var propertyObject = entity[propertyName];
if (!property.isNullable) {
propertyObject.extend({ required: true });
}
if (property.maxLength) {
propertyObject.extend({ maxLength: property.maxLength });
}
}
for (var i = 0; i < entityType.foreignKeyProperties.length; i++) {
var property = entityType.foreignKeyProperties[i];
console.log(property);
var propertyName = property.name;
var propertyObject = entity[propertyName];
if (!property.isNullable) {
propertyObject.extend({ required: true });
}
if (property.maxLength) {
propertyObject.extend({ maxLength: property.maxLength });
}
//Bussines rule
propertyObject.extend({ notEqual: 0 });
}
}
};
return {
constructor: constructor,
initializer: initializer
};
})();
この関数を初期化子として使用します。
store.registerEntityTypeCtor("Indicador", domain.indicador.constructor, domain.indicador.initializer);
それはほんの始まりに過ぎませんが、当面は私にとって役に立ちます。
アップデート:
検証を追加する方法を変更しました。誰かに役立つ場合に備えて、ここで共有します。
ヘルパーオブジェクト:
app.validatorHelper = (function (breeze) {
var foreignKeyInvalidValue = 0;
function addDataTypeRules(dataType, property) {
switch (dataType) {
case breeze.DataType.DateTime:
//TODO: implement my function to validate dates. This validator is too permissive
property.extend({ date: true });
break;
case breeze.DataType.Int64:
case breeze.DataType.Int32:
case breeze.DataType.Int16:
//it's needed to accept negative numbers because of the autogenerated keys
property.extend({ signedDigit: true });
break;
case breeze.DataType.Decimal:
case breeze.DataType.Double:
case breeze.DataType.Single:
property.extend({ number: true });
break;
}
};
function addValidationRules(entity) {
var entityType = entity.entityType;
if (entityType) {
for (var i = 0; i < entityType.dataProperties.length; i++) {
var property = entityType.dataProperties[i];
//console.log(property);
var propertyName = property.name;
var propertyObject = entity[propertyName];
addDataTypeRules(property.dataType, propertyObject);
if (!property.isNullable) {
propertyObject.extend({ required: true });
}
if (property.maxLength) {
propertyObject.extend({ maxLength: property.maxLength });
}
}
for (var i = 0; i < entityType.foreignKeyProperties.length; i++) {
var property = entityType.foreignKeyProperties[i];
//console.log(property);
var propertyName = property.name;
var propertyObject = entity[propertyName];
addDataTypeRules(property.dataType, propertyObject);
if (!property.isNullable) {
propertyObject.extend({ required: true });
//Bussiness Rule: 0 is not allowed for required foreign keys
propertyObject.extend({ notEqual: foreignKeyInvalidValue });
}
if (property.maxLength) {
propertyObject.extend({ maxLength: property.maxLength });
}
}
}
};
return {
addValidationRules: addValidationRules
};
})(breeze);
カスタムバリデーター:
(function (ko) {
ko.validation.rules['signedDigit'] = {
validator: function (value, validate) {
if (!validate) return true;
return ko.validation.utils.isEmptyVal(value) || (validate && /^-?\d+$/.test(value));
},
message: 'Please enter a digit'
};
ko.validation.registerExtenders();
})(ko);
イニシャライザでヘルパーを使用する:
app.domain.valorIndicador = (function (vHelper) {
"use strict";
var constructor = function () {
};
var initializer = function indicadorInitializer(entity) {
vHelper.addValidationRules(entity);
};
return {
constructor: constructor,
initializer: initializer
};
})(app.validatorHelper);
そして、初期化子を設定します。
store.registerEntityTypeCtor("ValorIndicador", domain.valorIndicador.constructor, domain.valorIndicador.initializer);
ノックアウトを使用してbreezejsからの検証エラーをバインドする簡単な方法。
entityAspectからvalidationErrorsChangedイベントをサブスクライブできます。
function subscribeValidation() {
return self.entity().entityAspect.validationErrorsChanged.subscribe(function (validationChangeArgs) {
validationChangeArgs.added.forEach(function (item) { addError(item); });
validationChangeArgs.removed.forEach(function (item) { self.validationErrors.remove(item); });
});
}
this.hasError = function (propertyName) {
var array = self.validationErrors();
var match = array.filter(function (item) {
return item.propertyName == propertyName;
});
if (match.length > 0) {
return true;
} else return false;
};
function addError(item) {
self.validationErrors.remove(function (i) {
return i.propertyName == item.propertyName;
});
self.validationErrors.push(item);
}
最後に、UIのメッセージにバインドできます(私はTwitter Boostrap cssクラスを使用しています)
<div class="control-group" data-bind="css: { 'error': hasError('Nome') }">
<label class="control-label">Nome</label>
<div class="controls">
<input type="text" class="input-xxlarge" data-bind="value: model().Nome">
<span class="help-inline" data-bind="text: getErrorMessage('Nome')"></span>
</div>
</div>
ここで完全な要点を参照してください
ノックアウトでそよ風を使い始めたとき、私は以前にこれを検索しました、そしてそれから私はものを検証する方法と検証をインラインで表示する方法についてまったく同じ質問をしました。
そよ風にはすでに検証が組み込まれていることを考慮して、観測可能な値が変更されるたびに検証結果を表示するカスタムノックアウトバインディングを作成することにしました。結局、それは非常に簡単でした。
カスタムバインディングは次のとおりです。
ko.bindingHandlers.breezeValidate = {
init: function (element, valueAccessor, allBindingsAccessor, context) {
var isOk = context.entityAspect.validateProperty(valueAccessor());
var errors = context.entityAspect.getValidationErrors(valueAccessor());
var message = "";
if (errors.length > 0)
message = errors[0].errorMessage;
$(element).html(message);
},
//update the control when the view model changes
update: function (element, valueAccessor, allBindingsAccessor, context) {
debugger;
this.init(element, valueAccessor, allBindingsAccessor, context)
}
};
そして、使用法は次のようになります。
<span data-bind="text: Name"></span>
<span data-bind="breezeValidate: 'Name'"></span>
これは、次の行のために機能します。
var isOk = context.entityAspect.validateProperty(valueAccessor());
そよ風がプロパティを検証するように要求されると、オブザーバブルを呼び出すことになり、ノックアウトによって登録されるため、変更されるたびに、このバインディングが再度呼び出され、それに応じてエラーメッセージが更新されます。
最初の検証メッセージを表示しているだけですが、もちろん、それらすべてを繰り返し処理したり、要素に別のスタイルを追加したりすることもできます。
お役に立てれば!!
なぜ人々がko.validationを使いたがるのかわからない-それはとにかくそよ風のクライアント側が行っている処理を複製するだけだ。そして、そよ風の開発者が検証がすぐにさらに強力になるだろうというヒントを考えると、なぜわざわざ。
それで私はThiagoOliveiraの素晴らしい仕事から始めました。しかし、私は最小限のマークアップが欲しかったのです。ブートストラップクラスの使用を想定し、前の要素の検証プロパティ名をデフォルトにすることで、ほとんどのマークアップの追加を単純化できます。
<span class="help-inline" data-bind="breezeValidation: null"></span>
勝つ!
私のko.bindingHandler:
//Highlight field in red & show first validation message
//
//Outputs first validation message for 'propertyName' or if null: previous controls value binding
//Needs ancestor with 'control-group' class to set class 'error' for Bootstrap error display
//
//Example:
//<td class="control-group">
// <input class="input-block-level text-right" data-bind="value: id" />
// <span class="help-inline" data-bind="breezeValidation: null"></span>
//</td>
//
//Does not and cannot validate keys that already exist in cache. knockout write calls breeze which throws uncaught error
ko.bindingHandlers.breezeValidation = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
// This will be called when the binding is first applied to an element
// Set up any initial state, event handlers, etc. here
var $msgElement = $(element);
var entity = viewModel;
var propName = valueAccessor();
if (propName === null) {
// $element.prev().data("bind") = "value: itemType"
var prevBinds = $msgElement.prev().data("bind");
if (!prevBinds) {
$msgElement.text("Could not find prev elements binding value.");
return;
}
var bindPhrases = prevBinds.split(/,/);
for (var i = 0, j = bindPhrases.length; i < j; i++) {
var bindPhrase = bindPhrases[i];
if (utility.stringStartsWith(bindPhrase, 'value: ')) {
propName = bindPhrase.substr(7);
break;
}
}
}
if (!propName) {
$msgElement.text("Could not find this or prev elements binding value.");
return;
}
//var $groupElement = $msgElement.parent();
var $groupElement = $msgElement.closest(".control-group");
if (!$groupElement.hasClass("control-group")) {
$msgElement.text("Could not find parent with 'control-group' class.");
return;
}
onValidationChange(); //fire immediately (especially for added)
//... and anytime validationErrors are changed fire onValidationChnange
entity.entityAspect.validationErrorsChanged.subscribe(onValidationChange);
element.onchange = function () {
//Should never have updates pushed from validation msgElement
$msgElement.text("readonly error");
};
function onValidationChange() {
var errors = entity.entityAspect.getValidationErrors(propName);
var message = "";
if (errors.length > 0) {
message = errors[0].errorMessage;
}
if (message) {
$groupElement.addClass('error');
}
else {
$groupElement.removeClass('error');
}
$msgElement.text(message);
}
}
//Not interested in changes to valueAccessor - it is only the fieldName.
//update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
};
単純な暗黙のプロパティの使用例を表示します。
<div class="control-group">
<label class="control-label" for="editStatusNote">Status note:</label>
<div class="controls">
<input id="editStatusNote" type="text" data-bind="value: statusNote" />
<span class="help-inline" data-bind="breezeValidation: null"></span>
</div>
</div>
ビューの明示的なプロパティの使用例:
<div class="control-group">
<label class="control-label" for="editAmount">Amount:</label>
<div class="controls">
<div class="input-prepend">
<span class="add-on">$</span>
<input id="editAmount" class="input-small" type="text" data-bind="value: amount" />
</div>
<span class="help-inline" data-bind="breezeValidation: 'amount'"></span>
</div>
</div>
breezeValidationをBootstrap3に更新し、マルチパスプロパティのサポートで改善しました。
ko.bindingHandlers.breezeValidation = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
// This will be called when the binding is first applied to an element
// Set up any initial state, event handlers, etc. here
var $msgElement = $(element);
var entity = viewModel;
var propName = valueAccessor();
if (propName === null) {
// $element.prev().data("bind") = "value: itemType"
var prevBinds = $msgElement.prev().data("bind");
if (!prevBinds) {
$msgElement.text("Could not find prev elements binding value.");
return;
}
var bindPhrases = prevBinds.split(/,/);
for (var i = 0, j = bindPhrases.length; i < j; i++) {
var bindPhrase = bindPhrases[i];
if (bindPhrase.substr(0, 7) == 'value: ') {
propName = bindPhrase.substr(7);
entity = ko.utils.unwrapObservable(entity);
var propPath = propName.replace(/[()]/g, "").split('.'), i = 0;
var tempProp = entity[propPath[i]], links = propPath.length;
i++;
while (ko.utils.unwrapObservable(tempProp) && i < links) {
entity = ko.utils.unwrapObservable(tempProp);
tempProp = entity[propName = propPath[i]];
i++;
}
break;
}
}
}
if (!propName) {
$msgElement.text("Could not find this or prev elements binding value.");
return;
}
//var $groupElement = $msgElement.parent();
var $groupElement = $msgElement.closest(".form-group");
if (!$groupElement.hasClass("form-group")) {
$msgElement.text("Could not find parent with 'form-group' class.");
return;
}
onValidationChange(); //fire immediately (especially for added)
//... and anytime validationErrors are changed fire onValidationChnange
entity.entityAspect.validationErrorsChanged.subscribe(onValidationChange);
element.onchange = function () {
//Should never have updates pushed from validation msgElement
$msgElement.text("readonly error");
};
function onValidationChange() {
var errors = entity.entityAspect.getValidationErrors(propName);
var message = "";
if (errors.length > 0) {
message = errors[0].errorMessage;
}
if (message) {
$groupElement.addClass('has-error');
}
else {
$groupElement.removeClass('has-error');
}
$msgElement.text(message);
}
}
//Not interested in changes to valueAccessor - it is only the fieldName.
//update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
};
ノックアウトバリデーターは、全体として簡単な検証を使用できます。
function addKoValidationRules(entity) {
if (entity.koValidationRulesAdded) {
return;
}
entity.entityType.dataProperties.forEach(function (property) {
entity[property.name].extend({
validation: {
validator: function () {
// manual validation ensures subscription to observables which current field depends on
// entity is added to context for retrieving other properties in custom validators
entity.entityAspect.validateProperty(property.name, { entity: entity });
var errors = entity.entityAspect.getValidationErrors(property.name);
if (!errors.length) {
return true;
}
this.message = errors[0].errorMessage;
return false;
},
message: ''
}
});
});
entity.koValidationRulesAdded = true;
}