ตรวจสอบฟิลด์หลังจากผู้ใช้ออกจากฟิลด์


93

ด้วย AngularJS ฉันสามารถใช้ng-pristineหรือng-dirtyตรวจสอบว่าผู้ใช้เข้ามาในฟิลด์หรือไม่ อย่างไรก็ตามฉันต้องการทำการตรวจสอบความถูกต้องฝั่งไคลเอ็นต์หลังจากที่ผู้ใช้ออกจากพื้นที่ฟิลด์แล้วเท่านั้น เนื่องจากเมื่อผู้ใช้เข้าสู่ช่องเช่นอีเมลหรือโทรศัพท์พวกเขามักจะได้รับข้อผิดพลาดเกิดขึ้นจนกว่าจะพิมพ์อีเมลฉบับเต็มเสร็จสิ้นและนี่ไม่ใช่ประสบการณ์ของผู้ใช้ที่ดีที่สุด

ตัวอย่าง


อัพเดท:

Angular มาพร้อมกับเหตุการณ์เบลอที่กำหนดเอง: https://docs.angularjs.org/api/ng/directive/ngBlur


3
ฉันเดาว่านี่เป็นสิ่งที่ angularjs ควรได้รับการสนับสนุน ...
csg

อาจจะ. การตรวจสอบความถูกต้องของฟอร์มในตัวทำงานได้ค่อนข้างดีซึ่งฉันไม่รู้ว่าฉันต้องการสิ่งนี้ในตัวหรือไม่ท้ายที่สุดกรณีการใช้งานส่วนใหญ่ฉันต้องการให้ Angular ตรวจสอบทันที เช่นในช่องตัวเลขฉันต้องการให้แบบฟอร์มยกเลิกทันทีหากผู้ใช้เริ่มพิมพ์ตัวอักษร
mcranston18

11
การตรวจสอบความเบลอเป็นเรื่องปกติตั้งแต่ 15 ปี ...
Olivvv

คำตอบ:


75

ตอนนี้ Angular 1.3 มีตัวเลือก ng-model และคุณสามารถตั้งค่าตัวเลือกเป็น{ 'updateOn': 'blur'}ตัวอย่างและคุณยังสามารถ debounce เมื่อการใช้งานพิมพ์เร็วเกินไปหรือคุณต้องการบันทึกการดำเนินการ DOM ที่มีราคาแพงบางส่วน (เช่นการเขียนโมเดล ไปยังสถานที่ DOM หลายแห่งและคุณไม่ต้องการให้วงจร $ แยกย่อยเกิดขึ้นในทุก ๆ ปุ่ม)

https://docs.angularjs.org/guide/forms#custom-triggersและhttps://docs.angularjs.org/guide/forms#non-immediate-debounce-model-updates

โดยค่าเริ่มต้นการเปลี่ยนแปลงเนื้อหาจะทริกเกอร์การอัปเดตแบบจำลองและการตรวจสอบแบบฟอร์ม คุณสามารถลบล้างพฤติกรรมนี้โดยใช้คำสั่ง ngModelOptions เพื่อผูกเฉพาะกับรายการเหตุการณ์ที่ระบุ เช่น ng-model-options = "{updateOn: 'เบลอ'}" จะอัปเดตและตรวจสอบความถูกต้องหลังจากตัวควบคุมสูญเสียโฟกัสเท่านั้น คุณสามารถตั้งค่าเหตุการณ์ต่างๆได้โดยใช้รายการที่คั่นด้วยช่องว่าง ได้แก่ ng-model-options = "{updateOn: 'mousedown Blur'}"

และ debounce

คุณสามารถชะลอการอัปเดต / การตรวจสอบโมเดลได้โดยใช้คีย์ debounce ด้วยคำสั่ง ngModelOptions ความล่าช้านี้จะใช้กับตัวแยกวิเคราะห์ตัวตรวจสอบความถูกต้องและแฟล็กโมเดลเช่น $ dirty หรือ $ pristine

เช่น ng-model-options = "{debounce: 500}" จะรอครึ่งวินาทีนับตั้งแต่การเปลี่ยนแปลงเนื้อหาครั้งสุดท้ายก่อนที่จะทริกเกอร์การอัปเดตแบบจำลองและการตรวจสอบแบบฟอร์ม


2
ในกรณีส่วนใหญ่วิธีนี้ไม่สามารถแก้ไขปัญหาได้เนื่องจากคุณอาจยังต้องการรับng-changeกิจกรรมก่อนที่ผู้ใช้จะออกจากฟิลด์ ตัวอย่างเช่นในกรณีของฉันฉันขอข้อมูลใหม่จาก API ของฉันทันทีที่ช่องป้อนข้อมูลถูกต้อง แต่ฉันไม่ต้องการแสดงข้อความแสดงข้อผิดพลาดทันทีที่ไม่ถูกต้อง - ฉันต้องการทำสิ่งนี้เมื่อเป็นเช่นนั้นเท่านั้น ไม่ถูกต้องและเกิดเหตุการณ์เบลอ
ทอม

@ ทอมฉันกำลังทดสอบ1.3.0 rc-3และมันไม่ทำงานchangeจนกว่าจะblurตรวจสอบสิ่งนี้: plnkr.co/edit/RivVU3r7xfHO1hUybqMu?p=preview
Gill Bates

นี่ควรเป็นคำตอบยอดนิยมที่ได้รับการยอมรับจริงๆ ทำไมต้องเพิ่มคำสั่งเมื่อคุณสามารถเพิ่มคุณสมบัติเดียวได้
Dan Hulton

92

จากเวอร์ชัน 1.3.0 สามารถทำได้อย่างง่ายดาย$touchedซึ่งเป็นจริงหลังจากผู้ใช้ออกจากฟิลด์

<p ng-show="form.field.$touched && form.field.$invalid">Error</p>

https://docs.angularjs.org/api/ng/type/ngModel.NgModelController


1
นั่นจะยังคงดำเนินการตรวจสอบความถูกต้องหลังจากการกดคีย์แต่ละครั้งหรือไม่? OP ระบุว่าเขาต้องการทำการตรวจสอบความถูกต้องหลังจากที่ผู้ใช้ออกจากสนามแล้วเท่านั้น
comecme

@comecme ใช่มันจะเป็นค่าเริ่มต้น ng-model-options updateOn: 'default'ซึ่งหมายความว่าสำหรับอินพุตคีย์อัพและสำหรับการเลือกเมื่อมีการเปลี่ยนแปลง
pocesar

5
ข้อบกพร่องใหญ่ที่นี่คือคุณจะไม่มีประสบการณ์การตรวจสอบความถูกต้องเหมือนเดิมหากคุณกลับมาที่สนาม คุณกลับมาที่กำลังสองซึ่งจะตรวจสอบความถูกต้องของการกดแป้นพิมพ์ทุกครั้งเนื่องจากได้ตั้งค่าให้แตะแล้ว ฉันประหลาดใจที่มีการโหวตเพิ่มขึ้นมากมาย
dudewad

3
@dudewad ถูกต้อง แต่ฉันคิดว่ามันเป็นพฤติกรรมที่ต้องการ UX อย่างชาญฉลาด - ถ้าคุณกลับมาที่ช่องที่ไม่ถูกต้องข้อความแสดงข้อผิดพลาดควรคงอยู่จนกว่าค่าจะถูกต้องไม่ใช่จนกว่าคุณจะพิมพ์ตัวอักษรตัวแรก
danbars

@dudewad จะไม่ช่วยให้ผู้ใช้ตรวจสอบความถูกต้องในการกดแต่ละครั้งหลังจากที่พวกเขารู้แล้วว่าฟิลด์ไม่ถูกต้องหลังจากที่กรอกครั้งแรก เมื่อพวกเขารู้ว่าฟิลด์ไม่ถูกต้องเป็นไปได้ว่าพวกเขาต้องการทราบอย่างรวดเร็วเมื่อฟิลด์นั้นถูกต้องและการตรวจสอบความถูกต้องของการกดปุ่มแต่ละครั้งจะทำให้กระบวนการนั้นเร็วขึ้น
Alan Dunning

32

ฉันแก้ไขสิ่งนี้โดยขยายสิ่งที่ @jlmcdonald แนะนำ ฉันสร้างคำสั่งที่จะนำไปใช้กับอินพุตและองค์ประกอบที่เลือกทั้งหมดโดยอัตโนมัติ:

var blurFocusDirective = function () {
    return {
        restrict: 'E',
        require: '?ngModel',
        link: function (scope, elm, attr, ctrl) {
            if (!ctrl) {
                return;
            }

            elm.on('focus', function () {
                elm.addClass('has-focus');

                scope.$apply(function () {
                    ctrl.hasFocus = true;
                });
            });

            elm.on('blur', function () {
                elm.removeClass('has-focus');
                elm.addClass('has-visited');

                scope.$apply(function () {
                    ctrl.hasFocus = false;
                    ctrl.hasVisited = true;
                });
            });

            elm.closest('form').on('submit', function () {
                elm.addClass('has-visited');

                scope.$apply(function () {
                    ctrl.hasFocus = false;
                    ctrl.hasVisited = true;
                });
            });

        }
    };
};

app.directive('input', blurFocusDirective);
app.directive('select', blurFocusDirective);

สิ่งนี้จะเพิ่มhas-focusและhas-visitedคลาสให้กับองค์ประกอบต่างๆเมื่อผู้ใช้โฟกัส / เข้าชมองค์ประกอบ จากนั้นคุณสามารถเพิ่มคลาสเหล่านี้ในกฎ CSS ของคุณเพื่อแสดงข้อผิดพลาดในการตรวจสอบความถูกต้อง:

input.has-visited.ng-invalid:not(.has-focus) {
    background-color: #ffeeee;   
}

สิ่งนี้ใช้ได้ดีในองค์ประกอบนั้นยังคงได้รับคุณสมบัติที่ไม่ถูกต้องเป็นต้น แต่ CSS สามารถใช้เพื่อให้ผู้ใช้ได้รับประสบการณ์ที่ดีขึ้น


2
เหตุใดจึงต้องใช้ '? ngModel' แทนที่จะเป็น 'ngModel' ฉันเข้าใจว่าต้องมี ngModel แต่ทำไมต้องมีเครื่องหมายคำถาม
Noremac

2
ขอบคุณสำหรับความคิด ควรชี้ให้เห็นว่าต้องใช้ jQuery (ฉันคิดว่า - มองไม่เห็น a .closest () ใน jql)
iandotkelly

1
ngModel ไม่รองรับองค์ประกอบ <input> ทั้งหมด (เช่น input type = submit) เดอะ? ต้องใช้ ngModel บนองค์ประกอบที่รองรับเท่านั้น
chmanie

5
formName.inputName.$dirtyจะซื่อสัตย์คุณไม่ควรเบี่ยงเบนจากทางเชิงมุมของการตั้งรัฐช่องใส่เช่น ฉันขอแนะนำให้แก้ไขคำสั่งเพื่อเขียนบูลีนที่คล้ายกันformName.inputName.$focusedแทนที่จะใช้ชื่อคลาสสำหรับเบลอและบูลีนสำหรับการตรวจสอบความถูกต้อง
ทอม

17

ฉันจัดการสิ่งนี้ด้วย CSS ง่ายๆ errorนี้ไม่จำเป็นต้องให้ข้อความผิดพลาดเป็นพี่น้องของท่านที่เกี่ยวข้องกับและว่าพวกเขามีระดับของ

:focus ~ .error {
    display:none;
}

หลังจากปฏิบัติตามข้อกำหนดทั้งสองนี้แล้วสิ่งนี้จะซ่อนข้อความแสดงข้อผิดพลาดใด ๆ ที่เกี่ยวข้องกับช่องป้อนข้อมูลที่เน้นซึ่งฉันคิดว่า angularjs ควรทำ ดูเหมือนการกำกับดูแล


2
อ่า นี่เป็นวิธีที่ชาญฉลาด ฉันชอบสิ่งนี้มากกว่าการเขียนจาวาสคริปต์เพื่อทำ
ดาดฟ้า

5
ข้อเสียของวิธีนี้คือเมื่อมีคนแก้ไขข้อผิดพลาดข้อผิดพลาดจะมองไม่เห็นอีกต่อไปซึ่งไม่เหมาะ วิธีที่ดีที่สุดคือข้อผิดพลาดจะไม่แสดงจนกว่าจะเบลอ แต่ไม่หายไปจนกว่าจะได้รับการแก้ไข
Chris Nicola

4

สิ่งนี้ดูเหมือนจะถูกนำไปใช้เป็นมาตรฐานในเชิงมุมเวอร์ชันใหม่ ๆ

คลาสที่ไม่ถูกแตะต้องและสัมผัสถูกตั้งค่าตามลำดับก่อนและหลังที่ผู้ใช้โฟกัสไปที่องค์ประกอบที่ตรวจสอบแล้ว

CSS

input.ng-touched.ng-invalid {
   border-color: red;
}

4

เกี่ยวกับวิธีแก้ปัญหาของ @ lambinator ... ฉันได้รับข้อผิดพลาดต่อไปนี้ใน angular.js 1.2.4:

ข้อผิดพลาด: [$ rootScope: inprog] $ Digest อยู่ระหว่างดำเนินการ

ฉันไม่แน่ใจว่าฉันทำอะไรผิดพลาดหรือว่านี่เป็นการเปลี่ยนแปลงใน Angular แต่การลบscope.$applyคำสั่งนั้นช่วยแก้ปัญหาได้และคลาส / สถานะยังคงได้รับการอัปเดต

หากคุณยังเห็นข้อผิดพลาดนี้ให้ลองทำดังต่อไปนี้:

var blurFocusDirective = function () {
  return {
    restrict: 'E',
    require: '?ngModel',
    link: function (scope, elm, attr, ctrl) {
      if (!ctrl) {
        return;
      }
      elm.on('focus', function () {
        elm.addClass('has-focus');
        ctrl.$hasFocus = true;
      });

      elm.on('blur', function () {
        elm.removeClass('has-focus');
        elm.addClass('has-visited');
        ctrl.$hasFocus = false;
        ctrl.$hasVisited = true;
      });

      elm.closest('form').on('submit', function () {
        elm.addClass('has-visited');

        scope.$apply(function () {
          ctrl.hasFocus = false;
          ctrl.hasVisited = true;
        });
      });
    }
  };
};
app.directive('input', blurFocusDirective);
app.directive('select', blurFocusDirective);

2

อาจได้ผลสำหรับคุณในการเขียนคำสั่งที่กำหนดเองที่ครอบคลุมวิธีการ javascript เบลอ () (และเรียกใช้ฟังก์ชันการตรวจสอบความถูกต้องเมื่อทริกเกอร์) มีปัญหาเชิงมุมที่มีตัวอย่างหนึ่ง (เช่นเดียวกับคำสั่งทั่วไปที่สามารถเชื่อมโยงกับเหตุการณ์อื่น ๆ ที่ Angular ไม่สนับสนุนโดยกำเนิด):

https://github.com/angular/angular.js/issues/1277

หากคุณไม่ต้องการไปเส้นทางนั้นตัวเลือกอื่นของคุณคือตั้งค่า $ watch บนฟิลด์และเรียกใช้การตรวจสอบความถูกต้องอีกครั้งเมื่อกรอกข้อมูลในฟิลด์


2

หากต้องการทราบคำตอบเพิ่มเติมคุณสามารถลดความซับซ้อนของการติดแท็กอินพุตได้โดยใช้คลาสหลอก CSS3 และทำเครื่องหมายเฉพาะฟิลด์ที่เข้าชมด้วยคลาสเพื่อชะลอการแสดงข้อผิดพลาดในการตรวจสอบความถูกต้องจนกว่าผู้ใช้จะสูญเสียโฟกัสที่ฟิลด์:

(ตัวอย่างต้องใช้ jQuery)

JavaScript

module = angular.module('app.directives', []);
module.directive('lateValidateForm', function () {
    return {
        restrict: 'AC',
        link: function (scope, element, attrs) {
            $inputs = element.find('input, select, textarea');

            $inputs.on('blur', function () {
                $(this).addClass('has-visited');
            });

            element.on('submit', function () {
                $inputs.addClass('has-visited');
            });
        }
    };
});

CSS

input.has-visited:not(:focus):required:invalid,
textarea.has-visited:not(:focus):required:invalid,
select.has-visited:not(:focus):required:invalid {
  color: #b94a48;
  border-color: #ee5f5b;
}

HTML

<form late-validate-form name="userForm">
  <input type="email" name="email" required />
</form>

2

ตามคำตอบของ @nicolas .. CSS ที่แท้จริงควรใช้เคล็ดลับมันจะแสดงข้อความแสดงข้อผิดพลาดเมื่อเบลอเท่านั้น

<input type="email" id="input-email" required
               placeholder="Email address" class="form-control" name="email"
               ng-model="userData.email">
        <p ng-show="form.email.$error.email" class="bg-danger">This is not a valid email.</p>

CSS

.ng-invalid:focus ~ .bg-danger {
     display:none;
}

นี่เป็นสิ่งที่ดี แต่ข้อความแสดงข้อผิดพลาดจะหายไปเมื่อผู้ใช้คลิกกลับในฟิลด์ซึ่งไม่เหมาะอย่างยิ่ง
Bennett McElwee

2

นี่คือตัวอย่างการใช้ ng-messages (มีในเชิงมุม 1.3) และคำสั่งที่กำหนดเอง

ข้อความตรวจสอบความถูกต้องจะแสดงเป็นภาพเบลอในครั้งแรกที่ผู้ใช้ออกจากช่องป้อนข้อมูล แต่เมื่อเขาแก้ไขค่าข้อความตรวจสอบความถูกต้องจะถูกลบออกทันที (ไม่เบลออีกต่อไป)

JavaScript

myApp.directive("validateOnBlur", [function() {
    var ddo = {
        restrict: "A",
        require: "ngModel",
        scope: {},
        link: function(scope, element, attrs, modelCtrl) {
            element.on('blur', function () {
                modelCtrl.$showValidationMessage = modelCtrl.$dirty;
                scope.$apply();
            });
        }
    };
    return ddo;
}]);

HTML

<form name="person">
    <input type="text" ng-model="item.firstName" name="firstName" 
        ng-minlength="3" ng-maxlength="20" validate-on-blur required />
    <div ng-show="person.firstName.$showValidationMessage" ng-messages="person.firstName.$error">
        <span ng-message="required">name is required</span>
        <span ng-message="minlength">name is too short</span>
        <span ng-message="maxlength">name is too long</span>
    </div>
</form>

ปล. อย่าลืมดาวน์โหลดและรวม ngMessages ไว้ในโมดูลของคุณ:

var myApp = angular.module('myApp', ['ngMessages']);

1

ng-model-options ใน AngularJS 1.3 (เบต้าในขณะที่เขียนนี้) ได้รับการจัดทำเอกสารเพื่อรองรับ {updateOn: 'เบลอ'} สำหรับเวอร์ชันก่อนหน้าสิ่งต่อไปนี้ใช้ได้กับฉัน:

myApp.directive('myForm', function() {
  return {
    require: 'form',
    link: function(scope, element, attrs, formController) {
      scope.validate = function(name) {
        formController[name].isInvalid
            = formController[name].$invalid;
      };
    }
  };
});

ด้วยเทมเพลตดังนี้:

<form name="myForm" novalidate="novalidate" data-my-form="">
<input type="email" name="eMail" required="required" ng-blur="validate('eMail')" />
<span ng-show="myForm.eMail.isInvalid">Please enter a valid e-mail address.</span>
<button type="submit">Submit Form</button>
</form>

1

ใช้สถานะฟิลด์$ สัมผัสฟิลด์ถูกแตะสำหรับสิ่งนี้ดังที่แสดงในตัวอย่างด้านล่าง

<div ng-show="formName.firstName.$touched && formName.firstName.$error.required">
    You must enter a value
</div>

0

คุณสามารถตั้งค่าคลาส has-error css แบบไดนามิก (สมมติว่าคุณใช้ bootstrap) โดยใช้ ng-class และคุณสมบัติในขอบเขตของคอนโทรลเลอร์ที่เกี่ยวข้อง

plunkr: http://plnkr.co/edit/HYDlaTNThZE02VqXrUCH?p=info

HTML:

<div ng-class="{'has-error': badEmailAddress}">
    <input type="email" class="form-control" id="email" name="email"
        ng-model="email" 
        ng-blur="emailBlurred(email.$valid)">
</div>

ตัวควบคุม:

$scope.badEmailAddress = false;

$scope.emailBlurred = function (isValid) {
    $scope.badEmailAddress = !isValid;
};

0

หากคุณใช้ bootstrap 3 และ lesscss คุณสามารถเปิดใช้งานการตรวจสอบความเบลอโดยใช้ข้อมูลโค้ดน้อยกว่าต่อไปนี้:

:focus ~ .form-control-feedback.glyphicon-ok {
  display:none;
}

:focus ~ .form-control-feedback.glyphicon-remove {
  display:none;
}

.has-feedback > :focus {
  & {
    .form-control-focus();
  }
}

0

ฉันใช้คำสั่ง นี่คือรหัส:

app.directive('onBlurVal', function () {
    return {
        restrict: 'A',
        link: function (scope, element, attrs, controller) {

            element.on('focus', function () {
                element.next().removeClass('has-visited');
                element.next().addClass('has-focus');
            });

            element.on('blur', function () {

                element.next().removeClass('has-focus');
                element.next().addClass('has-visited');
            });
        }
    }
})

ตัวควบคุมอินพุตทั้งหมดของฉันมีองค์ประกอบสแปนเป็นองค์ประกอบถัดไปซึ่งเป็นที่ที่แสดงข้อความการตรวจสอบความถูกต้องของฉันดังนั้นคำสั่งเป็นแอตทริบิวต์จึงถูกเพิ่มเข้าไปในตัวควบคุมอินพุตแต่ละตัว

ฉันยังมี (เป็นทางเลือก) คลาส css. has-focus และ has-visit ในไฟล์ css ของฉันซึ่งคุณเห็นว่าถูกอ้างถึงในคำสั่ง

หมายเหตุ: อย่าลืมเพิ่ม 'on-เบลอ-val' ด้วยวิธีนี้ในการควบคุมอินพุตของคุณโดยไม่มีเครื่องหมายวรรคตอน


0

คุณสามารถบรรลุเป้าหมายได้ด้วยการใช้ng-focus คุณต้องระบุ ng-focus ในช่องป้อนข้อมูลของคุณ และในขณะที่เขียนอนุพันธ์ของ ng-show คุณต้องเขียนตรรกะไม่เท่ากันด้วย เช่นเดียวกับรหัสด้านล่าง:

<input type="text" class="form-control" name="inputPhone" ng-model="demo.phoneNumber" required ng-focus> <div ng-show="demoForm.inputPhone.$dirty && demoForm.inputPhone.$invalid && !demoForm.inputPhone.$focused"></div>


0

เราสามารถใช้ฟังก์ชันออนโฟกัสและออนเบลอได้ จะง่ายและดีที่สุด

<body ng-app="formExample">
  <div ng-controller="ExampleController">
  <form novalidate class="css-form">
    Name: <input type="text" ng-model="user.name" ng-focus="onFocusName='focusOn'" ng-blur="onFocusName=''" ng-class="onFocusName" required /><br />
    E-mail: <input type="email" ng-model="user.email" ng-focus="onFocusEmail='focusOn'" ng-blur="onFocusEmail=''" ng-class="onFocusEmail" required /><br />
  </form>
</div>

<style type="text/css">
 .css-form input.ng-invalid.ng-touched {
    border: 1px solid #FF0000;
    background:#FF0000;
   }
 .css-form input.focusOn.ng-invalid {
    border: 1px solid #000000;
    background:#FFFFFF;
 }
</style>

ลองที่นี่:

http://plnkr.co/edit/NKCmyru3knQiShFZ96tp?p=preview

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.