English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
このプラグインはangular.js、JQueryを使用して実装しています。(jQueryのインクルードはangularの前に行われます)
ユーザーは、入力フィールドにデータを入力した後に必須項目、整数型、浮動小数点数の確認を行うことができます。
form内の入力フィールドの確認を行う場合、提出ボタンをクリック後に必須項目の確認を実行できます。
効果画像は以下の通りです:
(1)確認が通らなかった場合の背景赤、他のスタイルは以下の通りです
input.ng-invalid, select.ng-invalid { background-color: #ee82ee !important; border: 1px solid #CCC; }); .qtip { position: absolute; max-width: 260px; display: none; min-width: 50px; font-size: 10.5px; line-height: 12px; direction: ltr; }); .qtip-content { position: relative; padding: 5px 9px; overflow: hidden; text-align: left; word-wrap: break-word; }); .qtip-rounded, .qtip-tipsy { -moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; }); .qtipmodal-ie6fix { position: absolute !important; }); .box-shadow-tips { background-color: #F63; border-color: #F5A88F; color: white; -moz-box-shadow: 2px 2px 2px #969696; -webkit-box-shaow: 2px 2px 2px #969696; box-shadow: 2px 2px 2px #969696; });
angular.jsの内蔵確認が通らなかった場合、自動的にタグに.ngが追加されます。-invalidスタイルは、ここでこのスタイルを上書きしているため
input.ng-invalid, select.ng-invalid { background-color: #ee82ee !important; border: 1px solid #CCC; });
(2)HTMLコードは以下の通りです
<body ng-app="myApp"> <form name="baseInfoForm"> <div ng-controller="testCtrl"> <input type="text" ng-model="age" my-valid="r"><br> <input type="text" ng-model="name" my-valid="int fn:certCheck"><br> <input type="button" value="提交" ng-click="submit()"> </div> </form> </body>
(3)このプラグインはdirective myValidを使用して実装されています
app.directive('myValid', ['$parse', 'uiTipsFactory', 'uiValidFactory', function ($parse, tips, valid) { var uiValidAttrIdName = 'ui-valid-id'; return { restrict: 'A', require: 'ngModel', link: function (scope, el, attrs, ctrl) { var validId = el.attr(uiValidAttrIdName); if (!validId) { validId = Math.guid(); el.attr(uiValidAttrIdName, validId); }); var getRules = function () { return attrs.myValid; }); var lastOldRules; var validFn = function (value, oldRules) { var sp = '_'; var rules = getRules(); var r = valid.check(value, rules, scope, attrs.uiValidTips); if (lastOldRules && !oldRules) { oldRules = lastOldRules; }); if (r.flag && oldRules) { rules = rules &63; rules + "'" + oldRules : oldRules; }); if (rules) { var arrInner = rules.split(' '); var i = 0; for (; i < arrInner.length; i++) { var oneRule = arrInner[i]; if (!oneRule.trim()) { continue; }); ctrl.$setValidity(attrs.ngModel + sp + oneRule, r.flag &63; true : oneRule != r.rule); }); }); if (!r.flag) { tips.on(el, r.msg); else { tips.off(el); }); return r.flag; }); var init = function () { var rules = getRules(); if (!rules) { return; }); var parsers = ctrl.$parsers; if (parsers && parsers.length > 0) { parsers.clean(); }); parsers.unshift(function (value) { return validFn(value) &63; value : undefined; validFn(ctrl.$modelValue, oldRules); }); scope.$watch(attrs.ngModel, function (newVal, oldVal) { if (newVal === oldVal) { return; }); if (ctrl.$modelValue != undefined && (ctrl.$invalid || el.hasClass('ng')) {-invalid))) { validFn(ctrl.$modelValue); }); validFn(ctrl.$modelValue, oldRules); scope.$watch(getRules, function (newRules, oldRules) { init(); lastOldRules = oldRules; if (ctrl.$modelValue === undefined || ctrl.$modelValue === null) { var needValid = false; el.hasClass('ng-invalid'); var isValNaN = ctrl.$viewValue !== ctrl.$viewValue; if (ctrl.$invalid || (ctrl.$viewValue !== undefined && !isValNaN)) { needValid = true; }); if (needValid) { ctrl.$setViewValue(ctrl.$viewValue); }); else { if (!ctrl.$dirty && attrs.dirtyCheck) { console.log('----'); else { } }); }); validFn(ctrl.$modelValue, oldRules); }); }); });
attrs.ngModelを監視して、ルールrules、ctrl.$parserを通じて入力フィールドの内容変更に対応します。
このdirectiveを使用するときは、確認が通った後、入力フィールドにIDを動的に追加して、確認背景情報を変更するために使用します。
(4)確認ロジック処理 uiValidFactory
app.factory('uiValidFactory', ['$parse', 'uiTipsFactory', function ($parse, tips) { return { check: function (val, rules, $scope, defaultTips, extendParam) { if (!rules) { return { flag: true }); }); var rulesArr = rules.split(' ')} isBlank = val === null || val === undefined || val === '' || ('' + val === ''); //如果不是必填项 且没有输入值 则清除提示框 if ($.inArray('r', rulesArr) === -1 && isBlank) { return { flag: true }); }); var i = 0, len = rulesArr.length; for (; i < len; i++) { var rule = rulesArr[i]; if (!rule) { continue; }); var flag = true; if ('r' === rule) { //如果是必填项,有值 返回true flag = !isBlank; } else if (rule.contains(':')) { //如果校验规则是 fn:ctrl.certCheck flag = this.checkRule(val, rule.split(', ')/:/), $scope, extendParam); else { //校验 规则是 int 用正则匹配 数字 邮箱 长度 var pat = this.pats[rule]; if (pat instanceof RegExp) { if (angular.isString(val)) { flag = this.mat(val, pat); }); } else if (angular.isFunction(pat)) { flag = pat(val); else { flag = false; }); }); //これは何をしているのでしょうか if (angular.isString(flag)) { return { flag: false, msg: flag, rule: rule }); }); if (flag === false) { var msg = this.getMsg(rule, defaultTips) || this.getMsg('tips.valid'); console.log(msg); return { flag: false, msg: msg, rule: rule }); }); }); return { flag: true }); }, checkRule: function (val, ruleArr, $scope, extendParam) { //ruleArr fn:certCheck var rule = ruleArr[0]; if (rule === 'fn') {}} fnName = ruleArr[1]);//呼び出される関数の名前を指定します certCheck var fn = $parse(fnName)($scope); if (!fn) { return true; }); return fn.call($scope, val, extendParam); else { return true; }); }, checkValidForm: function (formName) { //必須項目のみをチェックします //属性フィルタを使用して内部のすべての要素を取得します var formContext = $('form[name="{0}"],[ng-form="{0}"],[data-ng-form="{0}"]'.format(formName)), validList = formContext.find('[my-valid]');//validList は配列ではありません、仮配列です if (!validList.length) { return; }); var that = this; validFlags = []; validList.each(function () { var ele = $(this), val = ele.val(), ruleStr = ele.attr('my-valid'); if (!ruleStr) { return true; }); if (angular.isString(val)) { val = val.trim(); }); var validRules = ruleStr.split(' '); if ($.inArray('r', validRules) != -1 && !val) { var modelValue = ele.attr('ng-model') || ele.attr('data-ng-model'); validFlags.push(modelValue); tips.on(ele, that.getMsg('r')); }); }); ); return validFlags; }, mat: function (val, pat) { if (!pat) { return; }); return pat.test(val); }); , getMsg: function (rule, tips) { tips = tips || ''; //可以在界面上直接書き込む tips if (tips && tips.contains(':')) { return tips; }); var msg = this.msgs[rule]; if (msg) { 63; tips.split(/:/)[0] : ''; var params1 = ''; var ruleArr = rule.split(/:/); params1 = ruleArr[ruleArr.length - 1]); }); return msg.format(params0, params1); else { }); }); , regPat: function (code, pat, msg) { if (this.pat[code]) { return; }); this.pats[code] = pat; this.msgs[code] = msg; }); , msgs: { 'r': '必須', 'int': '{0}は整数でなければなりません' }); , pats: { 'int': /^[\-\+]?([0-9]+)$/ }); }); }); ]) ;
、ele.myValidの確認ルールを取得して、
1、必須の場合、赤くしてこの入力フィールドを返し、マウスを上に持って行った場合に確認情報「必須」を表示します。
2、整数、浮動小数点数などの確認の場合、正規表現を通じて確認を行います。
3、最大(max)、最小(min)の場合、カスタムロジックを使用します。
4、fn確認があれば、対応するcontroller内の関数に基づいて確認を行います。
5、ユーザーが提出ボタンをクリックした場合、必須項目かどうかを確認し、確認が通らなかった場合、対応する要素の背景を赤くします。
(5)確認が通らなかった場合、提示Factory---uiTipsFactory
app.factory('uiTipsFactory', function () { return { filterClass: function (ele, invalid) { if (invalid) { //もし確認が通過しない場合 ele.removeClass('ng-valid').removeClass('ng-pristine').addClass('ng-invalid').addClass('ng-dirty'); else { ele.removeClass('ng-invalid').addClass('ng-valid'); }); }, on: function (ele, msg) { var lastTip = ele.data('last-tip'); if (lastTip && lastTip === msg) { return; }); ele.data('last-tip', msg); this.filterClass(ele, true); var offset = ele.offset(); if (!offset.top && !offset.left && ele.is('hidden')) { offset = ele.show().offset(); }); var id = ele.attr('ui-valid-id'); if (!id) { id = Math.guid(); ele.attr('ui-valid-id', id); }); if (id.contains('.')) { id = id.replace(/\./g, '_'); }); var top = offset.top; left = offset.left; var getTips = function () {}} var _tip = $('#vtip_ + id); if (_tip.length) { _tip.html(msg).css({ 'display': 'none', 'top': top + 'px', 'left': left + ele.width() + 10 + 'px' validFn(ctrl.$modelValue, oldRules); else { var html = '<div id="vtip_ + id + " class="vtip qtip qtip-rounded box-shadow-tips">' + <div class="qtip-content">' + msg + </div>'; $(html).css({ 'display': 'none', 'position': 'absolute', 'top': top + 'px', 'left': left + ele.width() + 10 + 'px' }).appendTo($('body')); }); }); var bindTipsShow = function () { getTips(); ele.unbind('mouseenter mouseleave').bind('mouseenter', function () { var _tip = $('#vtip_ + id); if (_tip.is(':hidden')) { _tip.show(); }); }).bind('mouseleave', function () { $('#vtip_ + id).hide(); validFn(ctrl.$modelValue, oldRules); }); bindTipsShow(); }, off: function (ele) { ele.data('last-tip', ''); this.filterClass(ele); var id = ele.attr('ui-valid-id'); if (!id) { return; }); if (id.contains('.')) { id = id.replace(/\./g, '_'); }); $('#vtip_ + id).remove(); ele.unbind('mouseenter mouseleave'); }); }); validFn(ctrl.$modelValue, oldRules);
1、確認が通らなかった場合、背景色を追加し、要素のCSS処理が以下の通りです。
ele.removeClass('ng-valid').removeClass('ng-pristine').addClass('ng-invalid').addClass('ng-dirty');
、確認が通った場合、以下のCSS処理が行われます。
ele.removeClass('ng-invalid').addClass('ng-valid');
2、背景ヒントテキストは、bodyにdivレイヤーを追加します。
(6)他の関連コード
var app = angular.module('myApp', []); app.controller('testCtrl', ['$scope', 'uiValidFactory', function ($scope, uiValidFactory) { $scope.certCheck = function (val) { if (val > 32) { return "数字太大了"; }); return true; }); $scope.submit = function () { if (!uiValidFactory.checkValidForm($scope.baseInfoForm.$name)) { }); }); }] ); Math.guid = function () { var a = "", b = 1; for (; b <= 32; b++) { var c = Math.floor(Math.random() * 16).toString(16); a += c; if (b === 8 || b === 12 || b === 16 || b === 20) { a += '-'; }); }); return a; }); String.prototype.contains = String.prototype.contains || function (a) { return this.indexOf(a) != -1; }); String.prototype.format = String.prototype.format || function () { var a = Array.prototype.slice.call(arguments); return this.replace(/\{(\d+})/g, function (c, b) { return a[b]; }) });
整个コード如下:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <script src="jquery-1.11.1.js"></script> <script src="angular.js"></script> <style type="text/css"> input.ng-invalid, select.ng-invalid { background-color: #ee82ee !important; border: 1px solid #CCC; }); .qtip { position: absolute; max-width: 260px; display: none; min-width: 50px; font-size: 10.5px; line-height: 12px; direction: ltr; }); .qtip-content { position: relative; padding: 5px 9px; overflow: hidden; text-align: left; word-wrap: break-word; }); .qtip-rounded, .qtip-tipsy { -moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; }); .qtipmodal-ie6fix { position: absolute !important; }); .box-shadow-tips { background-color: #F63; border-color: #F5A88F; color: white; -moz-box-shadow: 2px 2px 2px #969696; -webkit-box-shaow: 2px 2px 2px #969696; box-shadow: 2px 2px 2px #969696; }); </style> </head> <body ng-app="myApp"> <form name="baseInfoForm"> <div ng-controller="testCtrl"> <input type="text" ng-model="age" my-valid="r"><br> <input type="text" ng-model="name" my-valid="int fn:certCheck"><br> <input type="button" value="提交" ng-click="submit()"> </div> </form> </body> <script type="text/javascript"> var app = angular.module('myApp', []); app.controller('testCtrl', ['$scope', 'uiValidFactory', function ($scope, uiValidFactory) { $scope.certCheck = function (val) { if (val > 32) { return "数字太大了"; }); return true; }); $scope.submit = function () { if (!uiValidFactory.checkValidForm($scope.baseInfoForm.$name)) { }); }); }] ); Math.guid = function () { var a = "", b = 1; for (; b <= 32; b++) { var c = Math.floor(Math.random() * 16).toString(16); a += c; if (b === 8 || b === 12 || b === 16 || b === 20) { a += '-'; }); }); return a; }); String.prototype.contains = String.prototype.contains || function (a) { return this.indexOf(a) != -1; }); String.prototype.format = String.prototype.format || function () { var a = Array.prototype.slice.call(arguments); return this.replace(/\{(\d+})/g, function (c, b) { return a[b]; }) }); app.factory('uiTipsFactory', function () { return { filterClass: function (ele, invalid) { if (invalid) { //もし確認が通過しない場合 ele.removeClass('ng-valid').removeClass('ng-pristine').addClass('ng-invalid').addClass('ng-dirty'); else { ele.removeClass('ng-invalid').addClass('ng-valid'); }); }, on: function (ele, msg) { var lastTip = ele.data('last-tip'); if (lastTip && lastTip === msg) { return; }); ele.data('last-tip', msg); this.filterClass(ele, true); var offset = ele.offset(); if (!offset.top && !offset.left && ele.is('hidden')) { offset = ele.show().offset(); }); var id = ele.attr('ui-valid-id'); if (!id) { id = Math.guid(); ele.attr('ui-valid-id', id); }); if (id.contains('.')) { id = id.replace(/\./g, '_'); }); var top = offset.top; left = offset.left; var getTips = function () {}} var _tip = $('#vtip_ + id); if (_tip.length) { _tip.html(msg).css({ 'display': 'none', 'top': top + 'px', 'left': left + ele.width() + 10 + 'px' validFn(ctrl.$modelValue, oldRules); else { var html = '<div id="vtip_ + id + " class="vtip qtip qtip-rounded box-shadow-tips">' + <div class="qtip-content">' + msg + </div>'; $(html).css({ 'display': 'none', 'position': 'absolute', 'top': top + 'px', 'left': left + ele.width() + 10 + 'px' }).appendTo($('body')); }); }); var bindTipsShow = function () { getTips(); ele.unbind('mouseenter mouseleave').bind('mouseenter', function () { var _tip = $('#vtip_ + id); if (_tip.is(':hidden')) { _tip.show(); }); }).bind('mouseleave', function () { $('#vtip_ + id).hide(); validFn(ctrl.$modelValue, oldRules); }); bindTipsShow(); }, off: function (ele) { ele.data('last-tip', ''); this.filterClass(ele); var id = ele.attr('ui-valid-id'); if (!id) { return; }); if (id.contains('.')) { id = id.replace(/\./g, '_'); }); $('#vtip_ + id).remove(); ele.unbind('mouseenter mouseleave'); }); }); validFn(ctrl.$modelValue, oldRules); app.factory('uiValidFactory', ['$parse', 'uiTipsFactory', function ($parse, tips) { return { check: function (val, rules, $scope, defaultTips, extendParam) { if (!rules) { return { flag: true }); }); var rulesArr = rules.split(' ')} isBlank = val === null || val === undefined || val === '' || ('' + val === ''); //如果不是必填项 且没有输入值 则清除提示框 if ($.inArray('r', rulesArr) === -1 && isBlank) { return { flag: true }); }); var i = 0, len = rulesArr.length; for (; i < len; i++) { var rule = rulesArr[i]; if (!rule) { continue; }); var flag = true; if ('r' === rule) { //如果是必填项,有值 返回true flag = !isBlank; } else if (rule.contains(':')) { //如果校验规则是 fn:ctrl.certCheck flag = this.checkRule(val, rule.split(', ')/:/), $scope, extendParam); else { //校验 规则是 int 用正则匹配 数字 邮箱 长度 var pat = this.pats[rule]; if (pat instanceof RegExp) { if (angular.isString(val)) { flag = this.mat(val, pat); }); } else if (angular.isFunction(pat)) { flag = pat(val); else { flag = false; }); }); //これは何をしているのでしょうか if (angular.isString(flag)) { return { flag: false, msg: flag, rule: rule }); }); if (flag === false) { var msg = this.getMsg(rule, defaultTips) || this.getMsg('tips.valid'); console.log(msg); return { flag: false, msg: msg, rule: rule }); }); }); return { flag: true }); }, checkRule: function (val, ruleArr, $scope, extendParam) { //ruleArr fn:certCheck var rule = ruleArr[0]; if (rule === 'fn') {}} fnName = ruleArr[1]);//呼び出される関数の名前を指定します certCheck var fn = $parse(fnName)($scope); if (!fn) { return true; }); return fn.call($scope, val, extendParam); else { return true; }); }, checkValidForm: function (formName) { //必須項目のみをチェックします //属性フィルタを使用して内部のすべての要素を取得します var formContext = $('form[name="{0}"],[ng-form="{0}"],[data-ng-form="{0}"]'.format(formName)), validList = formContext.find('[my-valid]');//validList は配列ではありません、仮配列です if (!validList.length) { return; }); var that = this; validFlags = []; validList.each(function () { var ele = $(this), val = ele.val(), ruleStr = ele.attr('my-valid'); if (!ruleStr) { return true; }); if (angular.isString(val)) { val = val.trim(); }); var validRules = ruleStr.split(' '); if ($.inArray('r', validRules) != -1 && !val) { var modelValue = ele.attr('ng-model') || ele.attr('data-ng-model'); validFlags.push(modelValue); tips.on(ele, that.getMsg('r')); }); }); ); return validFlags; }, mat: function (val, pat) { if (!pat) { return; }); return pat.test(val); }); , getMsg: function (rule, tips) { tips = tips || ''; //可以在界面上直接書き込む tips if (tips && tips.contains(':')) { return tips; }); var msg = this.msgs[rule]; if (msg) { 63; tips.split(/:/)[0] : ''; var params1 = ''; var ruleArr = rule.split(/:/); params1 = ruleArr[ruleArr.length - 1]); }); return msg.format(params0, params1); else { }); }); , regPat: function (code, pat, msg) { if (this.pat[code]) { return; }); this.pats[code] = pat; this.msgs[code] = msg; }); , msgs: { 'r': '必須', 'int': '{0}は整数でなければなりません' }); , pats: { 'int': /^[\-\+]?([0-9]+)$/ }); }); }); ]) ; app.directive('myValid', ['$parse', 'uiTipsFactory', 'uiValidFactory', function ($parse, tips, valid) { var uiValidAttrIdName = 'ui-valid-id'; return { restrict: 'A', require: 'ngModel', link: function (scope, el, attrs, ctrl) { var validId = el.attr(uiValidAttrIdName); if (!validId) { validId = Math.guid(); el.attr(uiValidAttrIdName, validId); }); var getRules = function () { return attrs.myValid; }); var lastOldRules; var validFn = function (value, oldRules) { var sp = '_'; var rules = getRules(); var r = valid.check(value, rules, scope, attrs.uiValidTips); if (lastOldRules && !oldRules) { oldRules = lastOldRules; }); if (r.flag && oldRules) { rules = rules &63; rules + "'" + oldRules : oldRules; }); if (rules) { var arrInner = rules.split(' '); var i = 0; for (; i < arrInner.length; i++) { var oneRule = arrInner[i]; if (!oneRule.trim()) { continue; }); ctrl.$setValidity(attrs.ngModel + sp + oneRule, r.flag &63; true : oneRule != r.rule); }); }); if (!r.flag) { tips.on(el, r.msg); else { tips.off(el); }); return r.flag; }); var init = function () { var rules = getRules(); if (!rules) { return; }); var parsers = ctrl.$parsers; if (parsers && parsers.length > 0) { parsers.clean(); }); parsers.unshift(function (value) { return validFn(value) &63; value : undefined; validFn(ctrl.$modelValue, oldRules); }); scope.$watch(attrs.ngModel, function (newVal, oldVal) { if (newVal === oldVal) { return; }); if (ctrl.$modelValue != undefined && (ctrl.$invalid || el.hasClass('ng')) {-invalid))) { validFn(ctrl.$modelValue); }); validFn(ctrl.$modelValue, oldRules); scope.$watch(getRules, function (newRules, oldRules) { init(); lastOldRules = oldRules; if (ctrl.$modelValue === undefined || ctrl.$modelValue === null) { var needValid = false; el.hasClass('ng-invalid'); var isValNaN = ctrl.$viewValue !== ctrl.$viewValue; if (ctrl.$invalid || (ctrl.$viewValue !== undefined && !isValNaN)) { needValid = true; }); if (needValid) { ctrl.$setViewValue(ctrl.$viewValue); }); else { if (!ctrl.$dirty && attrs.dirtyCheck) { console.log('----'); else { } }); }); validFn(ctrl.$modelValue, oldRules); }); }); }); </script> </html>
声明:本文の内容はインターネットから取得しており、著作権者は所有者であり、インターネットユーザーが自発的に貢献し、自己でアップロードしたものであり、本サイトは所有権を持ちません。また、人工編集は行われておらず、関連する法的責任も負いません。著作権侵害が疑われる内容がある場合は、メールを送信していただければ幸いです:notice#oldtoolbag.com(メールを送信する際に、#を@に置き換えてください。報告を行い、関連する証拠を提供してください。一旦確認がとれましたら、本サイトは即座に侵害が疑われるコンテンツを削除します。)