Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angularjs only last async call in directive actually executed

Note: I am using AngularJS 1.2

I have a form where each field will hit the server for validation on value changed.

The html snippet below (Assume for all purposes the html is set up correctly):



        <div class="col-sm-5">
            <input class="form-control input-sm" url="type:googleplus" ng-model="element.googleplus" type="text"><br />
            <input class="form-control input-sm" url="type:yelp" ng-model="element.yelp" type="text"><br />
            <input class="form-control input-sm" url="type:foursquare" ng-model="element.foursquare" type="text>
        </div>

In my javascript I've set up a directive that hooks into each field and attempts to hit the server as the value updates. The scenario that breaks the current functionality is when the form is loaded with populated values.

OnValueChanged calls Validate.Options multiple times with no pause in between. I expected the server to be hit for each Validation.Options call, but instead only the last call is executed.

AngularJS below:

...
.factory('Validation', ['va', function (va) { // 'va' is a throttled resource
    var result = va('/manage/api/validate?type=:type&input=:url',
    {
        type: '@type',
        url: '@url'
    },
    {
        options: {
            method: 'options',
            debounceMS: 0, // turned off debounce in lieu of this problem
            abortPending: false
        }
    });
    return result;
}])

...
.directive('url', ['$filter', 'Validation', '$q', function ($filter, Validation, $q) {
    return {
        require: 'ngModel',
        link: function (scope, elm, attrs, ctrl) {
            // parses some stuff
            var config = {};
            (attrs['url'] || '').split(' ').map(function (p) { var a = p.split(':'); config[a[0]] = a[1]; });
            if (config.type)
                config.type = config.type.split(',');
            config.isRequired = elm[0].required;

            function validateUrl(viewValue, type) {
                var deferred = $q.defer();
                Validation.options({ url: viewValue, type: type }, function (response) {
                    deferred.resolve(response.IsValid);
                });

                return deferred.promise;
            }

            function onValueChanged(viewValue) {
                // hits the server to check if the url is valid
                if (viewValue) {
                    var type = config.type ? config.type[0] : undefined;

                    validateUrl(viewValue, type).then(function (isValid) {
                        ctrl.$setValidity('url', isValid);
                    });

                }
                else {
                    // prevents saving the listing as field is invalid
                    ctrl.$setValidity('url', !config.isRequired);
                }

                return viewValue;
            }

            ctrl.$parsers.push(onValueChanged);

            // revalidate when the model updates
            scope.$watch(
                function () { return ctrl.$viewValue; },
                function (viewValue) { return onValueChanged(viewValue); }
            );
        }
    };
}])

Why would only the last call be executed? Popping open the network tab, I see exactly one server call with the type "foursquare".

like image 755
Jefferson Hwang Avatar asked May 20 '26 14:05

Jefferson Hwang


1 Answers

Figured it out! Had to do with the throttled resource...apparently setting the debounceMS to 0 wasn't enough to prevent it from executing the debouncing portion. I switched back to $resource and everything worked (but that also means the deboucing portion for the onValueChanged won't work).

like image 86
Jefferson Hwang Avatar answered May 22 '26 03:05

Jefferson Hwang