简体   繁体   English

Angular 库模块使用抽象类注入服务

[英]Angular library module inject service with abstract class

I have created an Angular Component Library, which I distribute via NPM (over Nexus) to several similar projects.我创建了一个 Angular 组件库,我通过 NPM(通过 Nexus)将其分发给几个类似的项目。 This contains a PageComponent , which in turn contains a FooterComponent and a NavbarComponent .这包含一个PageComponent ,后者又包含一个FooterComponent和一个NavbarComponent In NavbarComponent exists a button, which triggers a logout function.NavbarComponent存在一个按钮,它会触发logout功能。 This function is to be provided via a PageService of the respective project.此功能将通过相应项目的PageService提供。 For this purpose I created an AbstractPageService in the Angular Component library ( PageService extends AbstractPageService ).为此,我在 Angular 组件库( PageService extends AbstractPageService )中创建了一个AbstractPageService

At first I solved this via the EventEmitter .起初我通过EventEmitter解决了这个问题。 But since I had to provide a logout function for each new page, I wanted to solve this via one service per project.但是由于我必须为每个新页面提供一个logout功能,我想通过每个项目的一个服务来解决这个问题。 I pass the PageService (Project) with using the forRoot() method of Angular Component Library.我使用 Angular 组件库的forRoot()方法传递PageService (Project)。

Everything works as desired, but wanted to know if there is a better solution or if the solution is so recommendable at all?一切都按预期工作,但想知道是否有更好的解决方案,或者该解决方案是否值得推荐?

I have the following solution for this:我有以下解决方案:

Components Lib - components.module.ts组件库 - components.module.ts

import {ModuleWithProviders, NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {RouterModule} from '@angular/router';
import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';


import {NavbarComponent} from './layout/navbar/navbar.component';
import {PageComponent} from './layout/page/page.component';
import {PageHeaderComponent} from './components/page-header/page-header.component';
// ... others ...

@NgModule({
  imports: [
    CommonModule,
    RouterModule,
    FontAwesomeModule
  ],
  declarations: [
    NavbarComponent,
    PageComponent,
    // ... others ...
  ],
  exports: [
    NavbarComponent,
    PageComponent,
    // ... others ...
  ]
})
export class ComponentsModule {
  static forRoot(pageService): ModuleWithProviders {
    return {
      ngModule: ComponentsModule,
      providers: [
        {provide: 'PageService', useClass: pageService}
      ]
    };
  }
}

Component Lib - page.component.ts组件库 - page.component.ts

import {Component, EventEmitter, HostBinding, Inject, Input, Output} from '@angular/core';
import {AbstractPageService} from '../../services/abstract-page.service';

@Component({
  selector: 'dc-page',
  templateUrl: './page.component.html',
  styleUrls: ['./page.component.scss']
})
export class PageComponent {

  @HostBinding('class') styleClass = 'd-flex flex-column';

  @Input() customStyleClass = null;

  @Input() showLogoutButton = true;
  // @Output() logoutButtonClick: EventEmitter<any> = new EventEmitter();

  constructor(@Inject('PageService') protected pageService: AbstractPageService) {
  }

  logout(): void {
    this.pageService.logout();
  }
}

Component Lib - abstract-page.service.ts组件库 - abstract-page.service.ts

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

@Injectable()
export abstract class AbstractPageService {

  abstract logout(): void;

}

And here the use in a project:这里在一个项目中使用:

Project - app.module.ts项目 - app.module.ts

import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';

import {AppComponent} from './app.component';
import {RouterModule, Routes} from '@angular/router';
import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';

import {ComponentsModule} from 'components';

const appRoutes: Routes = [
  {path: '', component: AppComponent},

  // otherwise redirect to home
  {path: '**', redirectTo: ''}
];


@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    FontAwesomeModule,
    RouterModule.forRoot(appRoutes),
    ComponentsModule.forRoot(PageService),
  ],
  providers: [
    // {provide: 'PageService', useClass: PageService}
  ],
  bootstrap: [AppComponent]
})
export class AppModule {
}

Project - page.service.ts项目 - page.service.ts

import {Injectable} from '@angular/core';
import {AbstractPageService} from '../../../projects/components/src/lib/services/abstract-page.service';

@Injectable({
  providedIn: 'root'
})
export class PageService extends AbstractPageService {

  constructor() {
    super();
  }

  logout() {
    console.log('Ausloggen!');
  }
}

I used this approach but ran into one problem:我使用了这种方法,但遇到了一个问题:

The PageService is not the same singleton instance in the library as in the application. PageService与库中的单例实例与应用程序中的不同。 Because it is provided in multiple modules (application module and the library module), it creates a new instance for each module.因为它在多个模块(应用程序模块和库模块)中提供,所以它为每个模块创建一个新实例。

What I ended up doing was providing my service through a string in the application module:我最终做的是通过应用程序模块中的string提供我的服务:

// app.module.ts in the root application
providers: [
  {
    provide: 'PageService',
    useClass: PageService
  }
]

Then injecting the service where needed using @Inject()然后使用@Inject()在需要的地方注入服务

// anywhere in the root application where the service is needed
constructor(@Inject('PageService') private pageService: PageService) {}

Then, in the library , I created a simple interface:然后,在库中,我创建了一个简单的界面:

export interface AbstractPageService {
  logout(): void;
}

Then, I can simply inject the service in the library through the Inject() decorator and type it using the interface (without the need to implement the interface in the root application):然后,我可以简单地通过Inject()装饰器将服务注入库中并使用接口键入(无需在根应用程序中实现该接口):

// anywhere in the library where the service is needed
constructor(@Inject('PageService') private pageService: AbstractPageService) {}

Now both the root application and the library use the same singleton instance of the PageService , as provided in the root application.现在根应用程序库都使用根应用程序中提供的相同的PageService单例实例。

As Sunil Singh said, a normal abstract class is the solution (without @Injectable ).正如 Sunil Singh 所说,一个普通的抽象类是解决方案(没有@Injectable )。

export abstract class AbstractPageService {

  abstract logout(): void;

}

I think there should be 'useValue' instead of 'useClass'.我认为应该有“useValue”而不是“useClass”。

 export class ComponentsModule { static forRoot(pageService): ModuleWithProviders { return { ngModule: ComponentsModule, providers: [ {provide: 'PageService', useClass: pageService} ] }; } }

To reuse singleton重用单例

Provide in application appModule.ts在应用程序 appModule.ts 中提供

{ provide: 'currentCompanyService', useFactory: getCompanyServiceFactory, deps: [AppInitService]}

Create factory which just returns injected instance.创建仅返回注入实例的工厂。

export function getCompanyServiceFactory(appInitService: AppInitService): AppInitService {
 return appInitService;
}

Inject in library component注入库组件

public CustomLibraryComponent {

   private companyService: AppInitService;

   constructor(@Inject('currentEnvironment') environment: any,
               @Inject('currentCompanyService') companyService: AppInitService) {
       this.environment = environment;
       this.companyService = companyService;
   }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM