typescript - Angular kütüphane modülünde soyut sınıf ile servis enjeksiyonu


Bir Angular Bileşen Kütüphanesi oluşturdum ve bunu birkaç benzer projeye NPM (Nexus üzerinden) aracılığıyla dağıtıyorum. Bu kütüphane bir PageComponent içeriyor ve bu da sırasıyla bir FooterComponent ve bir NavbarComponent içeriyor. NavbarComponent içinde bir buton bulunuyor ve bu buton bir logout fonksiyonunu tetikliyor. Bu fonksiyon, ilgili projenin bir PageService aracılığıyla sağlanacak. Bu amaçla Angular Bileşen kütüphanesinde bir AbstractPageService oluşturdum (PageService, AbstractPageService'i genişletiyor).

İlk olarak bunu EventEmitter ile çözdüm. Ancak her yeni sayfa için bir logout fonksiyonu sağlamam gerektiğinden, bunu projedeki her bir servis üzerinden çözmek istedim. Angular Bileşen Kütüphanesi'nin forRoot() metodunu kullanarak PageService (Proje) gönderiyorum.

Her şey istediği gibi çalışıyor, ama daha iyi bir çözüm olup olmadığını veya bu çözümün tamamen tavsiye edilebilir olup olmadığını bilmek istedim.

Bunun için aşağıdaki çözümüm var:

Bileşenler Kütüphanesi - 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}
      ]
    };
  }
}

Bileşen Kütüphanesi - 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();
  }
}

Bileşen Kütüphanesi - abstract-page.service.ts

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

@Injectable()
export abstract class AbstractPageService {

  abstract logout(): void;

}

Ve işte bir projede kullanımı:

Proje - 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 {
}

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

Cevap #1

Bu yaklaşımı kullandım ama bir sorunla karşılaştım:

PageService, kütüphanedeki ile uygulamadaki aynı singleton örneği değildir. Çünkü birden fazla modülde sağlanmaktadır (uygulama modülü ve kütüphane modülü), her modül için yeni bir örnek oluşturur.

Sonunda yaptığım şey, hizmetimi uygulama modülünde bir string aracılığıyla sunmaktı:

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

Daha sonra gerekli yerlerde servisi @Inject() kullanarak enjekte etmek

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

Sonra, kütüphanede basit bir arayüz oluşturdum:

export interface AbstractPageService {
  logout(): void;
}

Sonra, servisi kütüphaneye Inject() dekoratörü aracılığıyla basitçe enjekte edebilir ve arayüzü kullanarak tipini belirleyebilirim (ana uygulamada arayüzü uygulama gereği olmadan):

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

Şimdi hem kök uygulama hem de kütüphane, kök uygulamada sağlanan PageService örneğinin aynı tekil örneğini kullanıyor.

İlgili makaleler