A guide on incorporating a component through services in Angular version 4

Recently diving into Angular 4, I embarked on a mission to craft a dynamic component that showcases a loading indicator while crucial content is being fetched - think login credentials or data for a chart. Steering away from utilizing plugins, my main goal was to unravel the intricacies of crafting such a feature myself.

Using the CLI command ng g component loading, I kickstarted the creation process for the component. Subsequently, I hatched a service responsible for toggling the visibility of this component at will.

Within loading.component.ts:

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-loading',
  templateUrl: './loading.component.html',
  styleUrls: ['./loading.component.scss']
})
export class LoadingComponent implements OnInit {

  private loading = false;
  constructor() { }

  ngOnInit() {
  }

  public showLoad(){
    this.loading = true;
  }
  
  public hideLoad(){
    this.loading = false;
  }

}

loading.component.html delves into the visual rendition:

<div class ="box-loading" *ngIf="loading">
     <div id="loading"></div>
</div>

The orchestration stems from loading.service.ts:

import { Injectable } from '@angular/core';
import { LoadingComponent } from '../../loading/loading.component';

@Injectable()
export class LoadingService {

  constructor(private loading: LoadingComponent) { }

  public showLoading(){
    this.loading.showLoad();
  }
  
  public hideLoading(){
    this.loading.hideLoad();
  }

}

Despite invoking the showLoading() method yielding no results, my troubleshooting led me to test within my login page, spawning an error message:

Error: Template parse errors:
'app-loading' is not a known element:...

In efforts to combat this dilemma, I appended CUSTOM_ELEMENTS_SCHEMA to both loading.module.ts and app.module.ts:

loading.module.ts:

import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { CommonModule } from '@common';
import { LoadingComponent } from './loading.component';

@NgModule({
    declarations: [LoadingComponent],
    exports: [LoadingComponent],
    imports: [LoadingComponent],
    schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class LoadingModule{}

Venturing forward, how can I seamlessly embed my component within the HTML framework? Does any standard practice stipulate the correct approach for achieving this integration successfully? Am I treading the right path towards my end goal?

Answer №1

To establish communication between a service and a component, you can create a subject in the service which the component can subscribe to. Here's how you can do it:

In the service file:

import { Injectable, EventEmitter } from '@angular/core';

@Injectable()
export class LoadingService {

  showLoading = new EventEmitter<state>();

  public showLoading(){
    this.showLoading.emit(true);
  }
  public hideLoading(){
    this.showLoading.emit(false);
  }

}

In the component.ts file, subscribe to the eventEmitter within the service.

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-loading',
  templateUrl: './loading.component.html',
  styleUrls: ['./loading.component.scss']
})
export class LoadingComponent implements OnInit {

  private loading = false;
  constructor(private loadingService: LoadingService) { }

  ngOnInit() {
      this.loadingService.showLoading.subscribe(
         (state) => {
           this.loading = state;
         }
      );
  }
}

Don't forget to add the LoadingService to the providers array in the AppModule.

...
@NgModule({
    declarations: [LoadingComponent],
    imports: [ //You don't declare your components here, only the external modules you use in your project// ],
    providers: [LoadingService]
})
...

Services are essential for handling data retrieval from servers, facilitating communication between components, and offering numerous other advantages. It's crucial to adhere to Angular design patterns when developing applications.

Answer №2

A more simplified approach would be to create a Loading component as shown below:

import {Component, Input, OnInit} from '@angular/core';

@Component({
    ...
})
export class LoadingComponentimplements OnInit {

    @Input() isLoading: boolean = false;

    constructor() {
    }

    ngOnInit() {
    }

}

You can include the mat-spinner from Angular Material in the HTML of the LoadingCompoenent

<div class="loading-shade" *ngIf="isLoading">
    <mat-spinner></mat-spinner>
</div>

Add the following CSS for styling:

.loading-shade {
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    background: rgba(0, 0, 0, 0.15);
    z-index: 1;
    display: flex;
    align-items: center;
    justify-content: center;
}

Make sure to register your LoadingCompoenent in the AppModule

@NgModule({
    declarations: [LoadingComponent]
})

To use the LoadingCompoenent, simply pass a loading variable as an @Input() in any component like this:

In the component's template where loading is required, add the selector for the LoadingCompoenent

// TYPESCRIPT

loading: boolean;

doStuff(){
  this.loading = true;
  ...
  this.loading = false;
}

// HTML

<app-loading [isLoading]="loading"></app-loading>

<h1>TITLE</h1>

Answer №3

To dynamically load your LoadingComponent, follow these steps:

1) First, in the AppModule, register the LoadingComponent under entryComponents:

 @NgModule({
    declarations: [LoadingComponent],
    entryComponents:  [LoadingComponent],
    providers: [LoadingService]
})

2) Next, in the LoadingService, implement the methods show and hide as shown below:

import {
    ApplicationRef, ChangeDetectorRef, Component,       ComponentFactoryResolver, ComponentRef, EmbeddedViewRef, Injector,
    } from '@angular/core';


@Injectable()
export class LoadingService {

  loadingCompRef: any;

  constructor(private loading: LoadingComponent,
               private componentFactoryResolver: ComponentFactoryResolver,
                private appRef: ApplicationRef,
                private injector: Injector,
) {}

  public showLoading(){
        // Create a component reference from the component
         this.loadingCompRef = this.componentFactoryResolver
            .resolveComponentFactory(component)
            .create(this.injector);

        // bind data to component’s inputs
        this.loadingCompRef.instance.loading= true;
    
        // Attach component to the appRef so that it's inside the ng component tree
        this.appRef.attachView(this.loadingCompRef.hostView);
        
        // Get DOM element from component
        const domElem = (this.loadingCompRef.hostView as EmbeddedViewRef<any>).rootNodes[0] as HTMLElement;

        // Append Loading DOM element to the body
        document.getElementById('my-loader').appendChild(domElem);

  }
  
  public hideLoading(){
      this.loadingCompRef.instance.loading= false;
      this.appRef.detachView(this.loadingCompRef.hostView);
      this.loadingCompRef.destroy();
  }

}

For more information on dynamically loading components in Angular, refer to: https://angular.io/guide/dynamic-component-loader

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

Utilizing Angular 2+ to effectively manipulate the HTML Document Object Model with JavaScript in order to execute scripts

I'm facing an issue with inserting a script into my Angular project that has a specific format. <script type="text/javascript" src="https://s3.tradingview.com/external-embedding/embed-widget-events.js"> { "width": "510", "height": "600", "impo ...

Looking for a JavaScript (Angular) event listener to trigger when closing pages and tabs

I am looking for an event that will only work when closing a page or tab, but should not be triggered when the page is refreshed. I am aware of the "beforeunload" event, but it also gets activated on page refresh. Below is the code snippet I am currently ...

The object's type remains a mystery

While working on implementing jwt authentication in Ionic, React with TypeScript, I faced a typescript error when trying to add a check in my App.tsx file after successful implementation. The error stated: Object is of type 'unknown' Below is ...

Redirecting on the server side in Angular 2

I am currently studying Angular 2 and attempting to develop an application using Angular with Spring. In a traditional J2EE MVC application, the client sends a request, the server processes it, and then redirects to another page. The decision to redirect ...

Error encountered: The database is not found during the migration creation process in MikroORM

Attempting to create migrations with mikroORM has been a challenge as I am unable to generate the table itself. The error message indicating that the database "crm" does not exist leaves me puzzled about what steps I may have missed. Here is the code snip ...

The data retrieved from the web API is not undergoing the necessary conversion process

I am facing an issue with a web API call where the property checkNumber is defined as a double on the API side, but I need it to be treated as a string in my TypeScript model. Despite having the property defined as a string in my model, it is being receive ...

Resolving the Issue of Premature Header Output in NodeAPI Using Angular

I encountered an error in my Node API while simply logging the request to the console: router.get('/booksByISBN', checkRole, async (req, res) => { console.log(req.params) return res.sendStatus(200); }); node:internal/e ...

Fatal Error: Unable to resolve dependencies for ProductListComponent

I'm currently developing an Angular 6 app and encountering an error when trying to inject a service into my component. The error message Uncaught Error: Can't resolve all parameters for ProductListComponent: (?) is not providing specific details, ...

The element is implicitly of type 'any' due to the fact that a 'string' expression cannot be used to index the Phaser type

I've seen this question before, but I'm still struggling to find a solution that fits my situation. In my file, I have defined certain values and want to loop through them. The issue arises in the following part of the code: preloadImages(){ ...

What could be the reason for receiving the error message "Unidentified property 'payload' cannot be read" ?

Currently, I am working on developing an application that involves search functionality. In this application, when the user inputs text into the search box, a post request is sent to the database (using mongoose) to check if the data exists. If a match is ...

Are there any @types available for browser extension objects that are interoperable?

I am in the process of developing a browser extension that would work seamlessly on Edge, Chrome, and Firefox by utilizing Typescript. After coming across an article discussing interoperable browser extensions, I stumbled upon a code snippet: window.brow ...

The custom native date adapter is facing compatibility issues following the upgrade of Angular/Material from version 5 to 6

In my Angular 5 application, I had implemented a custom date adapter as follows: import {NativeDateAdapter} from "@angular/material"; import {Injectable} from "@angular/core"; @Injectable() export class CustomDateAdapter extends NativeDateAdapter { ...

The implementation of enumerations in Angular's HTML pages

I'm working with an enum in Angular 13 and I want to use it in a component. Enum: export enum PropertyCode { ALPHA = 'ALPHA', BETA = 'BETA', ZETA = 'ZETA', } TS: import { Component, VERSION } from '@angular/c ...

When attempting to loop through an Observable that is of type array using *ngFor, the error message "Cannot read property 'subscribe' of undefined

I can't figure out why this isn't working, even though it seems straightforward. I'm using a service that gives me an observable. There are a few rxjs streams connected to it in the following way: search(searchTerm: string { this.search ...

My unique VSCode extension performs flawlessly during debugging, but encounters issues once installed

While debugging, my custom language extension for VSCode is functioning perfectly. However, once installed, the VSIX doesn't seem to include any TypeScript features. When I open the correct file extension type, it highlights everything and displays th ...

I can't see the title text on my Ionic button, what should I do to fix this issue?

I have been working on a login form using ngx-translate and all components are translating correctly except for the submit button. It seems that the translated text for the button does not display unless it is hardcoded or clicked on. I'm not sure if ...

Updating the state of the Store using NGRX

As I work on writing unit tests to evaluate the functionality of my NGRX store in Angular 10 with NGRX 10, I have been setting actions to manipulate the state as needed. Then, I run the test for the action and verify that it changes the state accordingly. ...

Utilizing npm link with a TypeScript-written module: a guide for seamless development?

I am currently in the process of developing a TypeScript and Webpack-based library. To facilitate the development of this library, I have set up a separate test project (written in JS) and connected the library using npm link <package-name>. Howeve ...

The service's EventEmmiter is failing to emit signals

As the service constructor is executed before the component, crmService.actualName.subscribe() will not happen until the next time I emit a value. Is there any way to emit the current value right when a new subscription is made? This would allow me to "r ...

A versatile method for transforming enums into arrays that can handle null values

Looking for a simpler way to create a TypeScript function that converts an enum to an array, including support for null values. Here's an example of what I'm trying to achieve: enum Color { RED = "Red", GREEN = "Green&qu ...