1

I'm new on Angular 4 and I'm trying to create a component that shows a loading box while some content is being loaded... like login, loading a chart, etc. I don't want to use plugins to do it because I want to learn how to do. I've created the component using the CLI command ng g component loading and then I created a service that calls a method to show or hide the component from view.

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

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

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();
  }

}

When I invoke the showLoading() method, nothing happens. So I decided to test the <app-loading></app-loading> on my login page, but I'm getting the following error.

ERROR Error: Uncaught (in promise): Error: Template parse errors:
'app-loading' is not a known element:
1. If 'app-loading' is an Angular component, then verify that it is part of this module.
2. If 'app-loading' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message. ("
    <div class="login-box card">
        <div class="card-body">
            [ERROR ->]<app-loading *ngIf="loading"></app-loading>
            <form class="form-horizontal floating-labels" id="log"): ng:///LoginModule/LoginComponent.html@4:3

I added the CUSTOM_ELEMENTS_SCHEMA to the ngModule in the loading.module.ts and in the app.module.ts as well.

loading.module.ts

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

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

How can I inject my component into a html page? Is there a best practice to do it? Am I on the right way?

2
  • the short answer is, you can't. You can't inject components as dependencies. But there are plenty alternatives, however. Commented Jun 13, 2018 at 19:06
  • What you need to do is add your component to the entryComponents array in your app.module.ts file or your feature module. angular.io/guide/entry-components Commented Jun 13, 2018 at 19:16

3 Answers 3

1

You can implement a subject inside the service and the component listen to it as follow:

in the service

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 inside 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;
         }
      );
  }
}

of course don't forget to add the LoadingService to the AppModule providers.

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

in general, the services are used to service the components, such as fetching data from the server, or communicate between the components and many other benefits, your way of thinking is not good practice for Angular design pattern, i guess you need to be familiar with angular architecture before start coding.

Sign up to request clarification or add additional context in comments.

2 Comments

hi , i tried this, but could you add the import of state or is variable, please?
@qleoz12 it's just the type of emission. In this case, it's boolean
0

An easier way is to implement a Loading component like this:

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

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

    @Input() isLoading: boolean = false;

    constructor() {
    }

    ngOnInit() {
    }

}

in HTML of LoadingCompoenent using the mat-spinner of Angular Material

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

in CSS

.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;
}

Then register your LoadingCompoenent in AppModule

@NgModule({
    declarations: [LoadingComponent]
})

And finally call it anywhere wyou want in your application just by giving as @Input() any loading variable defined in any component like this:

On top of the template in that component which is fetching something, add the selector of the LoadingCompoenent

// TYPESCRIPT

loading: boolean;

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

// HTML

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

<h1>TITLE</h1>

Comments

0

You can load your LoadingComponent dynamicaly like this:

1) in AppModule, register the LoadingComponent as entryComponents like this:

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

2) In LoadingService, implement the methods show and hide like this:

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(){
        // 1. 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;
    
        // 2. Attach component to the appRef so that it's inside the ng component tree
        this.appRef.attachView(this.loadingCompRef.hostView);

        // 3. Get DOM element from component
        const domElem = (this.loadingCompRef.hostView as EmbeddedViewRef<any>).rootNodes[0] as HTMLElement;

        // 4. Append Loding DOM element to the body
        // `my-loader` is the `HTML id` attribut where you want to append you loader 
        document.getElementById('my-loader').appendChild(domElem);

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

}

For more detail about Dynamic loading of components in Angular you can look here: https://angular.io/guide/dynamic-component-loader

1 Comment

component is undefined in your sample code but I unserstood is LoadingComponent, all right ?, i Teste this example and this throw me this kind of error Error: StaticInjectorError(AppModule)[Service -> MensajeComponent]:

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.