Aj1
Aj1

Reputation: 973

Angular Loading Indicator Custom element Directive

I have written custom angular element directive, which shows/hides loading indicator based on condition evaluated on service call. The directive is applied as element to element. The directive works fine, but the problem the content is of the applied element not hidding.

Here is my directive:

export interface ILoadingIndicatorAttr extends ng.IAttributes {
  efpLoadingIndicator: string;
}
 export interface ILoadingIndicatorscope extends ng.IScope {
   loading: boolean;
 }



export class LoadingIndicator implements ng.IDirective {
   public restrict: string = 'A';
   public replace: boolean = true;
   static $inject = ["$compile", "$templateRequest", "$timeout"];

constructor(public _compile: ng.ICompileService, private templateService: ng.ITemplateRequestService, private timeout: ng.ITimeoutService) {

};

link = (scope: ILoadingIndicatorscope, element: ng.IAugmentedJQuery, attrs: ILoadingIndicatorAttr, ngModel: ng.INgModelController): void => {
    var tempThis = this;
    var templateUrl: string = 'app/modules/common/directives/loadingIndicator/loadingIndicator.tmpl.html';
    attrs.$observe('efpLoadingIndicator', () => {
        if (attrs.efpLoadingIndicator == "true") {
            this.timeout(() => {
                if (attrs.efpLoadingIndicator == "true") {
                    scope.loading = true;
                    tempThis.resolveTemplate(templateUrl).then((html: ng.IAugmentedJQuery) => {
                        tempThis._compile(html)(scope).appendTo(element);                            
                    });
                }
                else {                      
                    scope.loading = false;
                }
            }, 1000);
        }
        else {
            scope.loading = false;
        }
    });
}

resolveTemplate = (templateUrl: string): ng.IPromise<ng.IAugmentedJQuery> => {
    return this.templateService(templateUrl).then((html: string) => {
        return angular.element(html);
    });
   }
}

Here is how it is applied:

 <div efp-loading-indicator ={{vm.isLoading}}>

  <select> </select>

 </div

I want to hide the content of div when loading indicator is present, show when loading indicator is off. I know i can use ng-show on child elements but don't want to do that since I want to reuse this directive.

Upvotes: 0

Views: 217

Answers (1)

Hugues Stefanski
Hugues Stefanski

Reputation: 1182

The problem is that this is not what you expect in the link function. At that point,this will be referring to the window object (and thus tempThis too).

If you need to use the link function, you have to rebind it to this in the constructor, where this is referring to the class:

export interface ILoadingIndicatorAttr extends ng.IAttributes {
   efpLoadingIndicator: string;
}

export interface ILoadingIndicatorscope extends ng.IScope {
   loading: boolean;
}

export class LoadingIndicator implements ng.IDirective {
   public restrict: string = 'A';
   public replace: boolean = true;
   link:(scope: ILoadingIndicatorscope, element: ng.IAugmentedJQuery, attrs: ILoadingIndicatorAttr, ngModel: ng.INgModelController)=> void

   static $inject = ["$compile", "$templateRequest", "$timeout"];

constructor(public _compile: ng.ICompileService, private templateService: ng.ITemplateRequestService, private timeout: ng.ITimeoutService) {
    this.link = this.myLink.bind(this);
};

myLink(scope: ILoadingIndicatorscope, element: ng.IAugmentedJQuery, attrs: ILoadingIndicatorAttr, ngModel: ng.INgModelController): void => {
    var tempThis = this;
    var templateUrl: string = 'app/modules/common/directives/loadingIndicator/loadingIndicator.tmpl.html';
    attrs.$observe('efpLoadingIndicator', () => {
        if (attrs.efpLoadingIndicator == "true") {
            this.timeout(() => {
                if (attrs.efpLoadingIndicator == "true") {
                    scope.loading = true;
                    tempThis.resolveTemplate(templateUrl).then((html: ng.IAugmentedJQuery) => {
                        tempThis._compile(html)(scope).appendTo(element);                            
                    });
                }
                else {                      
                    scope.loading = false;
                }
            }, 1000);
        }
        else {
            scope.loading = false;
        }
    });
}

resolveTemplate = (templateUrl: string): ng.IPromise<ng.IAugmentedJQuery> =>{
    return this.templateService(templateUrl).then((html: string) => {
        return angular.element(html);
    });
   }
}

Upvotes: 0

Related Questions