Directive for creating a custom loading indicator in Angular

I have created a custom Angular element directive that displays and hides a loading indicator based on a condition from a service call. The directive is used as an element within another element. While the directive itself works correctly, the issue is that the content within the applied element does not hide.

Below is my directive code:

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 you can apply it:

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

  <select> </select>

 </div>

The goal is to hide the content of the div when the loading indicator is visible and show the content when the loading indicator is hidden. Avoiding the use of ng-show on child elements allows for reusability of this directive.

Answer №1

The issue arises when this does not reference the expected object within the link function. Instead, at that specific moment, this points to the window object (and consequently, so does tempThis).

If you intend to utilize the link function, it is essential to redefine it for this in the constructor, where this signifies 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);
    });
}
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

"Unlock the power of NGXS by leveraging the raw state values

I'm having trouble locating an example in the NGXS documentation that demonstrates how to properly type the state object. Specifically, I am looking for guidance on typing the return value of the snapshot method of Store. For instance: this.store.sn ...

Tips for declaring a particular type during the process of destructuring in Typescript

I have my own custom types defined as shown below: export type Extensions = | '.gif' | '.png' | '.jpeg' | '.jpg' | '.svg' | '.txt' | '.jpg' | '.csv' | '. ...

Error: No routes found in Angular 5

I have developed an Angular 5 app and set up the following routing configuration in app-routing.module.ts file: import { ControlPanelComponent } from './dashboard/control-panel/control-panel.component'; import { MainComponent } from './dash ...

Unable to utilize ng-class with both dynamic values and conditions on the same div element

The function is not running properly<div id = "number_1" class="number" ng-click="selected='1'" ng-class="{active: selected=='1',firstactive: firstnumber=='1'}"><div class="number-text">1</div></div>how ...

Utilizing SailsJS and Nginx in tandem to secure external access in your API

Currently, my SailsJS application is running on a digitalocean droplet with a MEAN Stack setup using nginx. All requests are directed to my Angular frontend except for those targeting /api, which are forwarded to a port 1337 through a proxy_pass configurat ...

Is there a way to create a type interface that points to the structure of another type?

I am focused on developing a type interface that includes properties defined in another interface. Below is the schema definition for a model object in TypeScript: export interface ModelSchema { idAttribute: string; name: string; attributes: { ...

What is the correct way to properly enter a Svelte component instance variable?

Currently, I am delving into learning Svelte and specifically exploring how to bind to a component instance as demonstrated in this tutorial: As I progress through the tutorial, I am attempting to convert it to Typescript. However, I have encountered an ...

Angular does not permit the use of the property proxyConfig

Click here to view the image I encountered an issue when attempting to include a proxy config file in angular.json, as it was stating that the property is not allowed. ...

Can you explain how to utilize prop values for setting attributes in styled-components v4 with TypeScript?

Overview Situation: const Link = styled.a` border: solid 1px black; border-radius: 5px; padding: 5px; margin: 10px 5px; `; type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement>; const LinkAsButton = styled(Link).attrs<ButtonP ...

Can a map key value be converted into a param object?

I have a map containing key-value pairs as shown below: for (let controller of this.attributiFormArray.controls) { attributiAttivitaMap.set(controller.get('id').value, { value: controller.get('valoreDefault').value, mandatory ...

Error message "Cannot bind to 'name' because it is not a recognized native property" encountered in Ionic icon configuration

Currently, I am developing a mobile app using Ionic 2 and Angular 2. I encountered an issue when trying to use the [name] property in conjunction with Ionic icons and expressions like this: <icon item-right [name]="result.kind ==='song&apo ...

Combine Two Values within Model for Dropdown Menu

I am currently facing a situation where I have a select box that displays a list of users fetched from the backend. The select box is currently linked to the name property of my ng model. However, each user object in the list also contains an email proper ...

Utilizing AngularJS to Access NodeJS MongoDB Connection

Currently, I am in the process of setting up a small development environment which includes a node.js http server, a mongodb database, and a frontend built in angular.js. To facilitate development, I have created an account with MongoHQ and am able to acce ...

Step-by-step guide on invoking an asynchronous method in canActivate for Ionic and Angular

My objective is to acquire a token for authenticating users. I am utilizing the import { Storage } from '@ionic/storage-angular'; to store the data, but I am encountering an issue where the Storage methods only function in asynchronous mode. Her ...

Experience the magic of Materialize CSS SideNav

I'm currently attempting to implement the mobile side navigation provided by Materialize. I've managed to display the menu icon, but when I click on it, nothing happens. It seems like the function is not being triggered, and there are no errors s ...

Update the status of the reactive form control to invalid

I'm attempting to mark a form control as invalid, however, the following code snippet is not producing the desired result this.currencyForm.controls['currencyMaxSell'].setErrors({smallerThan: true}) ...

What is the best way to inform TypeScript that the output of the subscribe method should be recognized as an array containing elements of type

I'm facing a challenge understanding types while working with noImplicitAny and typescript in Angular 6. The compiler is indicating that the type of result is Object, even though I am certain it should be an array of type Manufacturer. Unable to assig ...

Showing fixed values inside directive view after successful injection

Looking for some answers about using constants in angularjs. Here are the constants defined in my app.js: ... angular .module('blocTime', ['firebase', 'ui.router']) .config(config) .constant(&apos ...

Encountering a Problem with TypeScript Decorators

I've been diving into TypeScript by working on TS-based Lit Elements and refactoring a small project of mine. However, I'm starting to feel frustrated because I can't seem to figure out what's wrong with this code. Even though it' ...

Utilizing a setup module for configuration purposes

In the process of developing my angular application, I have integrated several external modules to enhance its functionality. One crucial aspect of my final application is the configuration classes that store important values like URLs and message keys us ...