如何使用服务注入组件。 Angular 4
How to inject a component using services. Angular 4
我是 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()
方法时,没有任何反应。所以我决定在登录页面上测试 <app-loading></app-loading>
,但出现以下错误。
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
我将 CUSTOM_ELEMENTS_SCHEMA
添加到 loading.module.ts 和 app.module.ts 中的 ngModule。
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{}
如何将我的组件注入 html 页面?有最佳实践吗?我走对了吗?
您可以在服务中实现一个主题,组件监听它如下:
在服务中
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 体系结构。
您可以这样加载您的 LoadingComponent
动态:
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
更简单的方法是像这样实现加载组件:
import {Component, Input, OnInit} from '@angular/core';
@Component({
...
})
export class LoadingComponentimplements OnInit {
@Input() isLoading: boolean = false;
constructor() {
}
ngOnInit() {
}
}
在 LoadingCompoenent
的 HTML 中使用 Angular Material
的 mat-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]
})
最后,只需将在任何组件中定义的任何 loading
变量指定为 @Input()
,即可在您的应用程序中的任何地方调用它,如下所示:
在正在获取内容的组件中的模板之上,添加 LoadingCompoenent
的选择器
// TYPESCRIPT
loading: boolean;
doStuff(){
this.loading = true;
...
this.loading = false;
}
// HTML
<app-loading [isLoading]="loading"></app-loading>
<h1>TITLE</h1>
我是 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()
方法时,没有任何反应。所以我决定在登录页面上测试 <app-loading></app-loading>
,但出现以下错误。
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
我将 CUSTOM_ELEMENTS_SCHEMA
添加到 loading.module.ts 和 app.module.ts 中的 ngModule。
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{}
如何将我的组件注入 html 页面?有最佳实践吗?我走对了吗?
您可以在服务中实现一个主题,组件监听它如下:
在服务中
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 体系结构。
您可以这样加载您的 LoadingComponent
动态:
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
更简单的方法是像这样实现加载组件:
import {Component, Input, OnInit} from '@angular/core';
@Component({
...
})
export class LoadingComponentimplements OnInit {
@Input() isLoading: boolean = false;
constructor() {
}
ngOnInit() {
}
}
在 LoadingCompoenent
的 HTML 中使用 Angular Material
mat-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]
})
最后,只需将在任何组件中定义的任何 loading
变量指定为 @Input()
,即可在您的应用程序中的任何地方调用它,如下所示:
在正在获取内容的组件中的模板之上,添加 LoadingCompoenent
的选择器// TYPESCRIPT
loading: boolean;
doStuff(){
this.loading = true;
...
this.loading = false;
}
// HTML
<app-loading [isLoading]="loading"></app-loading>
<h1>TITLE</h1>