不能在 Angular 6 的子模块中使用 app 模块内声明的指令

Posted

技术标签:

【中文标题】不能在 Angular 6 的子模块中使用 app 模块内声明的指令【英文标题】:can't use directive declared inside app module in child modules in Angular 6 【发布时间】:2019-03-09 11:22:25 【问题描述】:

我正在使用Angular 6

我通过扩展 NbgPopover 声明了一个指令 StickyPopover 并添加了 declarationapp.module.ts

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

import  AppComponent  from './app.component';
import  AppRoutingModule  from './app-routing.module';
import  AuthLayoutComponent  from './layouts/auth-layout/auth-layout.component';
import AdminLayoutComponent from './layouts/admin-layout/admin-layout.component';
import FormsModule from '@angular/forms';
import RouterModule from '@angular/router';
import BrowserAnimationsModule from '@angular/platform-browser/animations';
import NgbModule from '@ng-bootstrap/ng-bootstrap';
import StickyPopoverDirective from './sticky-popover.directive';


@NgModule(
  declarations: [
    AppComponent,
    AuthLayoutComponent,
    AdminLayoutComponent,
    StickyPopoverDirective           // custom created directive
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule,
    FormsModule,
    HttpClientModule,
    RouterModule,
    NgbModule.forRoot(),
  ],
  providers: [],
  bootstrap: [AppComponent],
)
export class AppModule  

并且该指令的位置与app.module.ts文件的位置处于同一级别。

sticky-popover.directive.ts的内容是

import 
  ElementRef,
  Directive, Input, TemplateRef,
  EventEmitter,
  Renderer2,
  Injector,
  ComponentFactoryResolver,
  ViewContainerRef,
  NgZone, OnInit, OnDestroy
 from '@angular/core';

import  NgbPopover, NgbPopoverConfig  from '@ng-bootstrap/ng-bootstrap';

@Directive(
  selector: '[appStickyPopover]',
  exportAs: 'stickyPopover'
)
export class StickyPopoverDirective extends NgbPopover implements OnInit, OnDestroy 
  @Input() stickyPopover: TemplateRef<any>;

  popoverTitle: string;

  placement: 'auto' | 'top' | 'bottom' | 'left' | 'right' | 'top-left' | 'top-right' | 'bottom-left' |
    'bottom-right' | 'left-top' | 'left-bottom' | 'right-top' | 'right-bottom' | ('auto' | 'top' | 'bottom' |
    'left' | 'right' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'left-top' | 'left-bottom' |
    'right-top' | 'right-bottom')[];

  triggers: string;

  container: string;

  shown: EventEmitter<>;

  hidden: EventEmitter<>;

  ngpPopover: TemplateRef<any>;

  canClosePopover: boolean;

  toggle(): void 
    super.toggle();
  

  isOpen(): boolean 
    return super.isOpen();
  



  constructor(
    private _elRef: ElementRef,
    private _render: Renderer2,
    injector: Injector,
    componentFactoryResolver: ComponentFactoryResolver,
    private viewContainerRef: ViewContainerRef,
    config: NgbPopoverConfig,
    ngZone: NgZone
  ) 
    super(_elRef, _render, injector, componentFactoryResolver, viewContainerRef, config, ngZone, document);
    this.triggers = 'manual';
    this.popoverTitle = 'Permissions';
    this.container = 'body';
  

  ngOnInit(): void 
    super.ngOnInit();
    this.ngbPopover = this.stickyPopover;

    this._render.listen(this._elRef.nativeElement, 'mouseenter', () => 
      this.canClosePopover = true;
      this.open();
    );

    this._render.listen(this._elRef.nativeElement, 'mouseleave', (event: Event) => 
      setTimeout(() =>  if (this.canClosePopover)  this.close();  , 100);

    );

    this._render.listen(this._elRef.nativeElement, 'click', () => 
      this.close();
    );
  

  ngOnDestroy(): void 
    super.ngOnDestroy();
  

  open() 
    super.open();
    const popover = window.document.querySelector('.popover');
    this._render.listen(popover, 'mouseover', () => 
      this.canClosePopover = false;
    );

    this._render.listen(popover, 'mouseout', () => 
      this.canClosePopover = true;
      setTimeout(() =>  if (this.canClosePopover)  this.close();  , 0);
    );
  

  close() 
    super.close();
  


我有一个模块 SavedSearches 导入到 AdminModule 内部,该模块在 app.module.ts

内部进一步声明

当我在 SavedSearches 模块的模板中使用 StickyPopover 指令时,例如

<i class="fa fa-plus-circle" aria-hidden="true" appStickyPopover [popoverTitle]="additional" [autoClose]="true" data-placement="right"></i>

它给出了错误

can't bind to 'popoverTitle' since it isn't a known property of i

当我将指令移入 SavedSearches 模块并将其包含在 saved-searches.module.ts declaration 中时,它可以正常工作,没有任何错误。

但是我不能在另一个模块中使用它并且在其他模块中使用它会给出

StickyPopovoerDirective 是 2 个模块声明的一部分。 将它移到导入这两个模块的上层模块中。

【问题讨论】:

【参考方案1】:

该指令仅对AppModule可见,如果您想将其用于其他模块,您可以创建一个SharedModule。然后在声明和导出中添加StickyPopoverDirective

@NgModule(
  declarations: [StickyPopoverDirective],
  exports: [StickyPopoverDirective]    
)
export class SharedModule  

在此之后,您可以将SharedModule 导入另一个模块并在那里使用您的指令。

【讨论】:

如果您使用自定义指令的组件是应用程序模块的一部分,我的意思是只有一个模块,组件和指令都是应用程序模块的一部分?不适用于我,然后为指令创建共享模块,将此共享模块导入应用程序模块...仍然没有运气... 为什么不能从appModule中声明导出?

以上是关于不能在 Angular 6 的子模块中使用 app 模块内声明的指令的主要内容,如果未能解决你的问题,请参考以下文章

Angular 使用了错误的路由器插座

我可以为 Angular 6 中模块的所有子路由加载一个组件吗?

Angular 5 的子路由无法正常工作

使用 TypeScript ES6 模块和 RequireJS 注册 Angular 元素

如何在材料设计 Angular 6 中设置时间选择器?

在Angular 10中使用模块化路由时如何正确处理404