16

I'm trying to build a directive that change loading status on buttons for slow ajax calls. Basically the idea is to set an attribute "ng-loading" to a button element, and let the directive add the rest of stuff.

This is the html code:

<button class="btn btn-primary" name="commit" type="submit" ng-loading="signupLoading">
  Submit
</button>

And this is the directive code:

.directive('ngLoading', ['$compile',  function($compile) {
  return {
    restrict: 'A',
    replace: false,
    terminal: true,
    link: function(scope, element, attrs) {
      element.attr('ng-class', '{loading:' + attrs['ngLoading'] +'}');
      element.attr('ng-disabled', attrs['ngLoading']);
      element.append(angular.element('<img class="loading-icon" src="/assets/images/loading-icon.gif"/>'));
      $compile(element.contents())(scope);
    }
  };
}]);

It all looks correct in the rendered HTML, but the attributes added from the directive is not funcioning at all. I can move those attributes to the HTML code and everything works great, but that's quite some redundant code in many places.

I referenced the post Angular directive to dynamically set attribute(s) on existing DOM elements but it does not solve my problem.

Any comment/suggestion are welcome. Thanks in advance.

1 Answer 1

25

You don't need to recompile that directive if all you want is some DOM manipulation, you can add and remove class in regards to the changes of a scope property. You can use $watch instead.

JAVASCRIPT

.directive('ngLoading', function() {
  return function(scope, element, attrs) {
    var img = angular.element('<img class="loading-icon" src="/assets/images/loading-icon.gif"/>');
    element.append(img);
    scope.$watch(attrs.ngLoading, function(isLoading) {
       if(isLoading) {
         img.removeClass('ng-hide');
         element.addClass('loading');
         element.attr('disabled', '');
       } else {
         img.addClass('ng-hide');
         element.removeClass('loading');
         element.removeAttr('disabled');
       }
    });
  };
});

Note: Your code doesn't work because it compiles the contents of the elements, not the element itself, where you attach the attributes you have implemented.

try $compile(elem)(scope); and it should work properly, but I don't recommend it because each element with such directive will have to re-compile again.

UPDATE: before using $compile remove the attribute 'ngLoading' to the element to prevent infinite compilation.

elem.removeAttr('ng-loading');
$compile(elem)(scope);
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for the answer. This should solve my problem. Though I'm still wondering why my code does not work.
check my update, if you're wondering why our code doesn't work
Thanks for the help. I tried $compile(elem)(scope); it still doesn't work. It crashed my browser (Chrome) consistently.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.