【问题标题】:How to inject a component using services. Angular 4如何使用服务注入组件。角 4
【发布时间】:2018-11-23 10:13:27
【问题描述】:

我是 Angular 4 的新手,我正在尝试创建一个在加载某些内容时显示加载框的组件...例如登录、加载图表等。我不想使用插件这样做是因为我想学习如何去做。 我使用 CLI 命令 ng g component loading 创建了组件,然后创建了一个服务,该服务调用一个方法来显示或隐藏组件。

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

}

当我调用showLoading() 方法时,什么也没有发生。所以我决定在我的登录页面上测试&lt;app-loading&gt;&lt;/app-loading&gt;,但我得到了以下错误。

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

我在 loading.module.ts 和 app.module.ts 中的 ngModule 中添加了 CUSTOM_ELEMENTS_SCHEMA

加载.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{}

如何将我的组件注入 html 页面?有最佳做法吗?我走对了吗?

【问题讨论】:

  • 简短的回答是,你不能。您不能将组件作为依赖项注入。但是,还有很多选择。
  • 您需要做的就是将您的组件添加到您的app.module.ts 文件或功能模块中的entryComponents 数组中。 angular.io/guide/entry-components

标签: angular typescript angular-services angular-components


【解决方案1】:

你可以在服务内部实现一个主题,组件监听它如下:

在服务中

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

}

在component.ts文件中,订阅服务内的eventEmitter。

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

当然不要忘记将 LoadingService 添加到 AppModule 提供程序。

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

一般来说,服务是用来服务组件的,比如从服务器获取数据,或者组件之间的通信等很多好处,你的思维方式对Angular设计模式来说不是很好的做法,我猜你需要在开始编码之前熟悉 Angular 架构。

【讨论】:

  • 嗨,我试过了,但是你能添加状态的导入还是变量?
  • @qleoz12 这只是发射的类型。在这种情况下,它是布尔值
【解决方案2】:

您可以像这样加载您的LoadingComponent dynamicaly

1) 在AppModule 中,将LoadingComponent 注册为entryComponents,如下所示:

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

2) 在LoadingService 中,像这样实现方法显示和隐藏:

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

}

有关 Angular 中 Dynamic loading of components 的更多详细信息,您可以查看此处: https://angular.io/guide/dynamic-component-loader

【讨论】:

  • 组件在您的示例代码中未定义,但我不明白的是 LoadingComponent,好吗?,我测试了这个示例,这给我抛出了这种错误错误:StaticInjectorError(AppModule)[Service -> MensajeComponent]:
【解决方案3】:

一个更简单的方法是像这样实现一个加载组件:

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

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

    @Input() isLoading: boolean = false;

    constructor() {
    }

    ngOnInit() {
    }

}

LoadingCompoenent 的 HTML 中使用 Angular Materialmat-spinner

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

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

然后在AppModule注册你的LoadingCompoenent

@NgModule({
    declarations: [LoadingComponent]
})

最后,只需在任何组件中定义@Input()loading 变量,即可在应用程序中任意位置调用它:

在该组件中正在获取内容的模板顶部,添加 LoadingCompoenent 的选择器

// TYPESCRIPT

loading: boolean;

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

// HTML

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

<h1>TITLE</h1>

【讨论】:

    猜你喜欢
    • 2019-03-04
    • 2019-03-20
    • 2018-08-11
    • 1970-01-01
    • 2019-06-09
    • 2017-06-18
    • 2018-07-05
    • 1970-01-01
    • 2016-02-10
    相关资源
    最近更新 更多