0

I have an angular directive called "show-errors" that I use for form validation. This will apply the "has-error" css class to the containing div when validation fails. This works great in most cases, such as when a user leaves the input box (blur) or when the user clicks the continue button (this is a multi-step form). I trigger validation for all form elements on continue by broadcasting a 'show-errors-check-validity' message.

$rootScope.$broadcast('show-errors-check-validity');

Here is the directive:

angular
    .module('eStoreApp')
    .directive('showErrors', showErrors);

function showErrors($log, $rootScope, $timeout) {
    return {
        restrict: 'A',
        require: '^form',
        link: function(scope, el, attrs, formCtrl) {
            var domElement = el[0].querySelector("[name]");
            var angularElement = angular.element(domElement);
            var textBoxName = angularElement.attr('name');
            var d = new Date();
            var time = d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds();

            angularElement.bind('blur', function () {
                $log.debug(time + ': BLUR: Input validation: ' + textBoxName + '.  Valid = ' + !formCtrl[textBoxName].$invalid);
                el.toggleClass('has-error', formCtrl[textBoxName].$invalid);
            });

            scope.$on('show-errors-check-validity', function() {
                $log.debug(time + ': SHOW-ERRORS: Input validation: ' + textBoxName + '.  Valid = ' + !formCtrl[textBoxName].$invalid);
                el.toggleClass('has-error', formCtrl[textBoxName].$invalid);
            });
        }
    };
}

Here is how it is used in HTML:

<div class="form-group row" show-errors>
    <label class="col-sm-2 control-label">Zip Code</label>
    <div class="col-sm-10">
        <input type="text" name="zip" class="form-control" required ng-model="vm.checkoutData.data.shipping.zip">
        <p class="help-block" ng-if="shippingInformationForm.zip.$error.required">Zip Code is required.</p> 
    </div>
</div>

This all works fantastic until I try to trigger validation with a promise return, specifically $modal.open(). The user can open a modal that allows them to select a previously saved address which is populated on the form. When they do this, I want validation to trigger. Again I'm using the 'show-errors-check-validity' message. This hits the function in the directive, but the angularjs form $error values have not yet updated. If I call $rootScope.$digest() I get an error that a digest is in progress. How would I cause the directive to fire AFTER the angularjs $scope.shippingAddressForm.zip.$invalid has been updated?

The selectAddress() function below is in the controller of the section that contains the shippingAddressForm and all the form elements with the 'show-errors' directive.

function selectAddress() {
    var modalInstance = $modal.open({
        templateUrl: 'Scripts/Checkout/AddressSelector/addressSelector.html',
        controller: 'addressSelectorController',
        controllerAs: 'vm',
        size: 'sm',
        resolve: {
            addresses: function() {
                return shippingAddressService.getAll();
            }
        }
    });

    modalInstance.result.then(function (selectedAddress) {
        vm.checkoutData.data.shipping = selectedAddress;
        if (selectedAddress.id) {
           $scope.$emit('show-errors-check-validity');
        }
    }, function() {
        $log.info('Modal dismissed.');
    });
}
Ryan Langton
  • 6,294
  • 15
  • 53
  • 103
  • A bit confused. Opening modal doesn't resolve the result promise , the modalInstance.close() does. Code and explanation differ. Please confirm you want the `$emit` when it is opened ... if so use the `opened` promise – charlietfl Oct 01 '15 at 13:27
  • I want the $emit after the modal is closed - the user has selected from a list of saved addresses and clicks OK. The problem is that the AngularJS form $invalid values have not updated before the validation is triggered. – Ryan Langton Oct 01 '15 at 13:29
  • Since modal controller isn't in child scope of directive, `$emit` doesn't seem like proper event process https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$emit – charlietfl Oct 01 '15 at 13:35
  • It's $emit'ing to form fields on the page. These are the in the child scope of the controller. Also since there may be other forms on the page that I don't want to receive the event, $emit is the correct. – Ryan Langton Oct 01 '15 at 13:47
  • Try using `$timeout` to defer to next digest cycle – charlietfl Oct 01 '15 at 13:52
  • Using $timeout worked. I put it around $scope.$emit. Thanks! – Ryan Langton Oct 01 '15 at 14:09

0 Answers0