AngularJS使用$http自定义表单验证
AngularJS custom form validation using $http
我有一个像这样的表单:
<form name="myForm" ng-submit="saveDeployment()">
<input type="hidden" value="{{item.CloneUrl}}" name="cloneurl" />
<input type="hidden" value="{{Username}}" name="username" />
<input type="radio" name="deploymenttype" ng-model="item.deploymentType" value="azure" checked="checked">Azure
<br />
<input type="radio" name="deploymenttype" ng-model="item.deploymentType" value="ftp">FTP
<div id="azure" ng-show="item.deploymentType=='azure'">
<label for="azurerepo">Azure Git Repo</label>
<input type="text" name="azurerepo" ng-model="item.azurerepo" ng-class="{error: myForm.azurerepo.$invalid}" ng-required="item.deploymentType=='azure'" />
</div>
<div id="ftp" ng-show="item.deploymentType=='ftp'">
<label for="ftpserver">FTP Server</label>
<input type="text" name="ftpserver" ng-model="item.ftpserver" ng-class="{error: myForm.ftpserver.$invalid}" ng-required="item.deploymentType=='ftp'" />
<label for="ftppath">FTP Path</label>
<input type="text" name="ftppath" ng-model="item.ftppath" ng-class="{error: myForm.ftppath.$invalid}" ng-required="item.deploymentType=='ftp'" />
<label for="ftpusername">FTP Username</label>
<input type="text" name="ftpusername" ng-model="item.ftpusername" ng-class="{error: myForm.ftpusername.$invalid}" ng-required="item.deploymentType=='ftp'"/>
<label for="ftppassword">FTP Password</label>
<input type="password" name="ftppassword" ng-model="item.ftppassword" ng-class="{error: myForm.ftppassword.$invalid}" ng-required="item.deploymentType=='ftp'"/>
</div>
<input type="submit" value="Save" ng-disabled="myForm.$invalid"/>
</form>
它的设置,以便所需的字段和保存按钮都是工作一旦数据被输入。但是,我的部分验证将是"Is the user already registered?"
,我将使用输入的数据通过POST使用$http.
我应该把逻辑在saveDeployment()
函数或有一个更好的地方把它?
* 更新: *
我已经实现了下面这是应用于一个元素的属性,但它调用服务器/数据库的每次按键,我不喜欢:
app.directive('repoAvailable', function ($http, $timeout) { // available
return {
require: 'ngModel',
link: function (scope, elem, attr, ctrl) {
console.log(ctrl);
ctrl.$parsers.push(function (viewValue) {
// set it to true here, otherwise it will not
// clear out when previous validators fail.
ctrl.$setValidity('repoAvailable', true);
if (ctrl.$valid) {
// set it to false here, because if we need to check
// the validity of the email, it's invalid until the
// AJAX responds.
ctrl.$setValidity('checkingRepo', false);
// now do your thing, chicken wing.
if (viewValue !== "" && typeof viewValue !== "undefined") {
$http.post('http://localhost:12008/alreadyregistered',viewValue) //set to 'Test.json' for it to return true.
.success(function (data, status, headers, config) {
ctrl.$setValidity('repoAvailable', true);
ctrl.$setValidity('checkingRepo', true);
})
.error(function (data, status, headers, config) {
ctrl.$setValidity('repoAvailable', false);
ctrl.$setValidity('checkingRepo', true);
});
} else {
ctrl.$setValidity('repoAvailable', false);
ctrl.$setValidity('checkingRepo', true);
}
}
return viewValue;
});
}
};
});
你不需要在指令中做$http请求,更好的地方是控制器。
您可以在控制器- $scope.saveDeployment = function () { // here you make and handle your error on request ... };
中指定方法,您将保存错误范围,然后创建一个指令,将监视$scope.yourResponseObject
并根据它设置有效性。
另外,如果你需要像输入字段模糊的请求和错误之类的东西,你需要用elem.bind('blur', ...)
创建一个简单的指令,在那里你用回调调用$scope.saveDeployment
来处理有效性。
看一下示例,可能有类似的东西- https://github.com/angular/angular.js/wiki/JsFiddle-Examples
使用异步$http
ajax调用验证表单输入字段是一种常见的需求,但我还没有发现任何完整的、可重用的和易于使用的实现,所以我尽我最大的努力做一个。
此功能对于检查用户名、电子邮件或其他字段/列是否唯一特别有用,但是在许多其他用例中,必须通过ajax调用验证值(如您的示例)。
我的解决方案有以下特点:
- 接受"检查";从
$scope
调用$http
或任何类型的验证(同步或异步)的函数 - 接受一个"网关";
$scope
的功能,允许根据ngModel
的值或状态绕过检查。 - 废除"检查";函数的执行,直到用户停止输入
- 确保只使用最新的
$http
调用结果(如果多个被触发并无序返回)。 - 允许状态绑定,以便UI可以适当和方便地响应。
- 可自定义脱脱时间,检查/门函数,绑定名称和验证名称。
我的指令是pmkr-validate-custom (GitHub)
。它可以用于任何异步验证。我已经在几个版本中测试了它,最早的版本是1.1.5。
下面是一个使用Twitter Bootstrap的示例,其中我检查用户名是否唯一。
现场演示
<form name="the_form" class="form-group has-feedback">
<div ng-class="{'has-success':userNameUnique.valid, 'has-warning':userNameUnique.invalid}">
<label for="user_name">Username</label>
<input
name="user_name"
ng-model="user.userName"
pmkr-validate-custom="{name:'unique', fn:checkUserNameUnique, gate:gateUserNameUnique, wait:500, props:'userNameUnique'}"
pmkr-pristine-original=""
class="form-control"
>
<span ng-show="userNameUnique.valid" class="glyphicon glyphicon-ok form-control-feedback"></span>
<span ng-show="userNameUnique.invalid" class="glyphicon glyphicon-warning-sign form-control-feedback"></span>
<i ng-show="userNameUnique.pending" class="glyphicon glyphicon-refresh fa-spin form-control-feedback"></i>
<p ng-show="userNameUnique.valid" class="alert alert-success">"{{userNameUnique.checkedValue}}" is availiable.</p>
<p ng-show="userNameUnique.invalid" class="alert alert-warning">"{{userNameUnique.checkedValue}}" is not availiable.</p>
<button
ng-disabled="the_form.$invalid || the_form.user_name.$pristine || userNameUnique.pending"
class="btn btn-default"
>Submit</button>
</div>
</form>
样本控制器:// Note that this ought to be in a service and referenced to $scope. This is just for demonstration.
$scope.checkUserNameUnique = function(value) {
return $http.get(validationUrl+value).then(function(resp) {
// use resp to determine if value valid
return isValid; // true or false
});
}
// The directive is gated off when this function returns true.
$scope.gateUserNameUnique = function(value, $ngModel) {
return !value || $ngModel.$pristine;
};
如果我做任何改进,他们将是最新的GitHub上,但我也要把代码在这里为这个指令和它的依赖关系(可能不会更新)。我欢迎通过GitHub问题提出建议或问题!
angular.module('pmkr.validateCustom', [
'pmkr.debounce'
])
.directive('pmkrValidateCustom', [
'$q',
'pmkr.debounce',
function($q, debounce) {
var directive = {
restrict: 'A',
require: 'ngModel',
// set priority so that other directives can change ngModel state ($pristine, etc) before gate function
priority: 1,
link: function($scope, $element, $attrs, $ngModel) {
var opts = $scope.$eval($attrs.pmkrValidateCustom);
// this reference is used as a convenience for $scope[opts.props]
var props = {
pending : false,
validating : false,
checkedValue : null,
valid : null,
invalid : null
};
// if opts.props is set, assign props to $scope
opts.props && ($scope[opts.props] = props);
// debounce validation function
var debouncedFn = debounce(validate, opts.wait);
var latestFn = debounce.latest(debouncedFn);
// initially valid
$ngModel.$setValidity(opts.name, true);
// track gated state
var gate;
$scope.$watch(function() {
return $ngModel.$viewValue;
}, valueChange);
// set model validity and props based on gated state
function setValidity(isValid) {
$ngModel.$setValidity(opts.name, isValid);
if (gate) {
props.valid = props.invalid = null;
} else {
props.valid = !(props.invalid = !isValid);
}
}
function validate(val) {
if (gate) { return; }
props.validating = true;
return opts.fn(val);
}
function valueChange(val) {
if (opts.gate && (gate = opts.gate(val, $ngModel))) {
props.pending = props.validating = false;
setValidity(true);
return;
}
props.pending = true;
props.valid = props.invalid = null;
latestFn(val).then(function(isValid) {
if (gate) { return; }
props.checkedValue = val;
setValidity(isValid);
props.pending = props.validating = false;
});
}
} // link
}; // directive
return directive;
}
])
;
angular.module('pmkr.debounce', [])
.factory('pmkr.debounce', [
'$timeout',
'$q',
function($timeout, $q) {
var service = function() {
return debounceFactory.apply(this, arguments);
};
service.immediate = function() {
return debounceImmediateFactory.apply(this, arguments);
};
service.latest = function() {
return debounceLatestFactory.apply(this, arguments);
};
function debounceFactory(fn, wait) {
var timeoutPromise;
function debounced() {
var deferred = $q.defer();
var context = this;
var args = arguments;
$timeout.cancel(timeoutPromise);
timeoutPromise = $timeout(function() {
deferred.resolve(fn.apply(context, args));
}, wait);
return deferred.promise;
}
return debounced;
}
function debounceImmediateFactory(fn, wait) {
var timeoutPromise;
function debounced() {
var deferred = $q.defer();
var context = this;
var args = arguments;
if (!timeoutPromise) {
deferred.resolve(fn.apply(context, args));
// return here?
}
$timeout.cancel(timeoutPromise);
timeoutPromise = $timeout(function() {
timeoutPromise = null;
}, wait);
return deferred.promise;
}
return debounced;
}
function debounceLatestFactory(fn) {
var latestArgs;
function debounced() {
var args = latestArgs = JSON.stringify(arguments);
var deferred = $q.defer();
fn.apply(this, arguments).then(function(res) {
if (latestArgs === args) {
deferred.resolve(res);
}
}, function(res) {
if (latestArgs === args) {
deferred.reject(res);
}
});
return deferred.promise;
}
return debounced;
}
return service;
}
])
;
angular.module('pmkr.pristineOriginal', [])
.directive('pmkrPristineOriginal', [
function() {
var directive = {
restrict : 'A',
require : 'ngModel',
link: function($scope, $element, $atts, $ngModel) {
var pristineVal = null;
$scope.$watch(function() {
return $ngModel.$viewValue;
}, function(val) {
// set pristineVal to newVal the first time this function runs
if (pristineVal === null) {
pristineVal = $ngModel.$isEmpty(val) ? '' : val.toString();
}
// newVal is the original value - set input to pristine state
if (pristineVal === val) {
$ngModel.$setPristine();
}
});
}
};
return directive;
}
])
;
我的解决方案是取自Kosmetika的想法。
我使用了angular-ui
项目,并在控制器上设置了一个onBlur
回调,该回调通过$http
调用web服务。
将控制器/模型属性设置为true或false。
然后我有一个<span>
使用ng-show
来观察控制器/模型属性,所以当web服务返回时,它会显示用户信息
- 解析javascript表单验证器
- Jquery表单验证插件-如果选中复选框,如何在提交时执行某些操作
- 自定义表单验证和提交
- Javascript表单验证的缺点
- Jquery的表单验证不起作用
- 语义ui表单验证:图像url
- jQuery表单验证适用于Mozilla和Internet Explorer,但不适用于Chrome或Safari
- JavaScript表单验证-“;“错误”;不按需要工作
- Mailchimp内联表单验证不适用于JS加载的HTML
- jQuery/JS获胜't在表单验证后重定向
- jquery表单验证复选框错误显示
- javascript表单验证-数值和密码
- 我想知道如何在javascript中进行表单验证
- jQuery表单验证(如果==“执行此操作”)仅适用于第一次输入
- 使用记录保存的消息进行表单验证
- 表单验证工作不正常,在不检查条目的情况下继续
- 如何对未知表单元素进行表单验证
- 使用ajax、php和mysql进行表单验证
- JS表单验证未发生
- 表单验证jsphp路径