【问题标题】:Angular library module inject service with abstract class带有抽象类的 Angular 库模块注入服务
【发布时间】:2019-01-22 08:25:02
【问题描述】:

我创建了一个 Angular 组件库,我通过 NPM(通过 Nexus)将其分发给几个类似的项目。这包含一个PageComponent,而后者又包含一个FooterComponent 和一个NavbarComponent。在NavbarComponent 中存在一个按钮,它触发logout 函数。此功能将通过相应项目的PageService 提供。为此,我在 Angular 组件库中创建了一个 AbstractPageServicePageService 扩展了 AbstractPageService)。

起初我通过EventEmitter 解决了这个问题。但是由于我必须为每个新页面提供一个logout 函数,我想通过每个项目一个服务来解决这个问题。我使用 Angular 组件库的 forRoot() 方法传递了 PageService(项目)。

一切正常,但想知道是否有更好的解决方案,或者该解决方案是否值得推荐?

对此我有以下解决方案:

组件库 - 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}
      ]
    };
  }
}

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

组件库 - abstract-page.service.ts

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

@Injectable()
export abstract class AbstractPageService {

  abstract logout(): void;

}

这里是在一个项目中的使用:

项目 - 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 {
}

项目 - 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!');
  }
}

【问题讨论】:

  • 也许AbstractPageClass 会比服务更好、更干净。
  • 显然,没有人有解决方案,或者这是我的错误。
  • 是的,抽象是比发射器更好的解决方案,而且它是可行的。我在所有库中都使用抽象。
  • @SunilSingh 你能给我们举个例子吗

标签: angular typescript


【解决方案1】:

重用单例

在应用程序 appModule.ts 中提供

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

创建只返回注入实例的工厂。

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

注入库组件

public CustomLibraryComponent {

   private companyService: AppInitService;

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

【讨论】:

    【解决方案2】:

    我使用了这种方法,但遇到了一个问题:

    PageService 在库中与应用程序中的单例实例不同。 因为它在多个模块(应用程序模块和库模块)中提供,所以它为每个模块。

    我最终做的是通过应用程序模块中的string 提供我的服务:

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

    然后使用@Inject()在需要的地方注入服务

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

    然后,在中,我创建了一个简单的界面:

    export interface AbstractPageService {
      logout(): void;
    }
    

    然后,我可以简单地通过Inject()装饰器将服务注入库中,并使用接口键入(无需在根应用程序中实现接口):

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

    现在,根应用程序库都使用相同的 PageService 单例实例,正如根应用程序中提供的那样。

    【讨论】:

    • +1。感谢您的回答。给任何实施此解决方案的人的提示:我在消费模块的某些地方使用了具体服务(例如PageService并且我在库模块中使用了抽象服务。在 both 地方,我需要手动指定注入令牌以使 Angular 使用相同的实例(例如,在 app 模块中使用 @Inject('PageService') private pageService: PageService)。
    【解决方案3】:

    我认为应该有“useValue”而不是“useClass”。

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

    【讨论】:

      【解决方案4】:

      正如 Sunil Singh 所说,一个普通的抽象类是解决方案(没有@Injectable)。

      export abstract class AbstractPageService {
      
        abstract logout(): void;
      
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-12-02
        • 1970-01-01
        • 2020-12-22
        • 2016-07-14
        • 1970-01-01
        • 2021-09-07
        • 2018-06-18
        • 2020-06-13
        相关资源
        最近更新 更多