【问题标题】:knockout validation using breeze utility?使用微风实用程序进行淘汰赛验证?
【发布时间】:2012-11-19 16:34:42
【问题描述】:

是否有人编写了一个实用程序,将 Breeze 元数据(从实体框架数据属性中捕获)转换为敲除验证扩展(使用 knockout.validation)?

【问题讨论】:

标签: validation knockout.js breeze


【解决方案1】:

我做了一个函数,从实体中读取元数据并添加验证规则。

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

【讨论】:

【解决方案2】:

一种使用敲除绑定来自breathjs的验证错误的简单方法。

我们可以从 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>

查看完整要点here

【讨论】:

    【解决方案3】:

    我之前已经搜索过这个,因为我开始使用带有敲除的微风,然后我对如何验证内容以及如何显示内联验证有完全相同的问题。

    考虑到微风已经内置了验证,我决定编写一个自定义的 Knockout Binding 来在每次可观察值发生变化时显示验证结果,毕竟这很容易:

    这是自定义绑定:

        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());
    

    当微风被请求验证属性时,它最终会调用 observable 并通过敲除注册,因此每次更改时,都会再次调用此绑定并相应地更新错误消息。

    我只是展示了第一条验证消息,当然您可以遍历所有这些消息,甚至为元素添加不同的样式。

    希望这会有所帮助!!

    【讨论】:

      【解决方案4】:

      不知道为什么人们会想要使用 ko.validation - 它只是复制了处理微风的客户端正在做的事情。鉴于微风开发人员暗示验证将很快变得更加强大,何必费心呢。

      所以我从 Thiago Oliveira 的出色作品开始。但我希望有最低限度的标记。通过假设使用引导类并默认上一个元素的验证属性名称,我可以简化大多数标记添加:

      <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>                        
      

      【讨论】:

        【解决方案5】:

        我将breathValidation 更新为Bootstrap 3,并通过多路径属性支持进行了改进。

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

        【讨论】:

          【解决方案6】:

          Knockout 验证器可以作为一个整体使用微风验证:

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

          【讨论】:

            猜你喜欢
            • 2018-06-05
            • 1970-01-01
            • 2013-07-29
            • 1970-01-01
            • 2012-11-04
            • 2012-02-18
            • 2013-07-30
            • 2015-02-01
            • 2017-12-01
            相关资源
            最近更新 更多