如何扩展 NgbDropdown 或为其编写包装器?

How to extend NgbDropdown or write a wrapper for it?

基本上,对于所有下拉菜单,我希望每次 NgbDropDown 打开时 运行 myOpenFunction() 以及每次关闭下拉菜单时 运行 myCloseFunction()

我在想我可以编写一个自定义指令来扩展 NgbDropDown 和 运行 下拉列表前后的函数。像这样:

<div myCustomDropdown> <!-- using my own directive instead of ngbDropdown -->
  <button  ngbDropdownToggle>Toggle dropdown</button>
  <div ngbDropdownMenu>
    <button ngbDropdownItem>Action - 1</button>
    <button ngbDropdownItem>Another Action</button>
  </div>
</div>

到目前为止我已经尝试过基于此

@Directive({
  selector: '[myCustomDropdown]'
})
export class DropdownDirective extends NgbDropdown implements OnDestroy {


  constructor(_changeDetector: ChangeDetectorRef, config: NgbDropdownConfig, @Inject(DOCUMENT)  _document: any,
              _ngZone: NgZone, _elementRef: ElementRef<HTMLElement>, _renderer: Renderer2,
              @Optional() ngbNavbar: NgbNavbar) {
    super(_changeDetector, config, _document, _ngZone, _elementRef, _renderer, ngbNavbar);
  }


  ngOnDestroy() {
    super.ngOnDestroy();
  }

}

这会引发错误

    core.js:6185 ERROR Error: Uncaught (in promise): NullInjectorError: R3InjectorError(TestModule)[NgbDropdown -> NgbDropdown -> NgbDropdown -> NgbDropdown]: 
  NullInjectorError: No provider for NgbDropdown!

你可以只为下拉菜单创建一个包含所有配置的包装组件然后你可以在任何地方使用它作为 ngbDropdown

下拉菜单

<div ngbDropdown class="d-inline-block">
    <button class="btn btn-outline-primary" [id]="id" 
           ngbDropdownToggle>Toggle dropdown</button>
    <div ngbDropdownMenu [attr.aria-labelledby]="id">
        <button 
               *ngFor="let item of items" 
               ngbDropdownItem 
               (click)="clickHandler(item.action)" >
           {{item.label}}
         </button>
    </div>
</div>

组件

export class DropdownComponent implements OnInit {
  @Input() items =[];
  constructor() { }

  clickHandler(action){
    if (action){
      action()
    }
  }
}

now you have a reusable compoennt base of ngbDropdown can accept qan items list with an callback

应用程序模板

<app-dropdown [items]="dropDownItems"></app-dropdown>

demo

另一种方法是创建指令并使用 HostBinding

@Directive({
  selector: '[extend-dropdown]'
})
export class ExtendDropdown {
  @Input('extend-dropdown')func:(boolean)=>any;
  @HostListener('openChange',['$event']) openChange(event)
  {
    console.log('inner function',event)
    this.func && this.func(event);
  }
}

你用作

<div ngbDropdown [extend-dropdown]="open" class="d-inline-block">
  ...
</div>

//If you has a function in .ts
open(event)
{
   console.log("from main",event)
}

<div ngbDropdown extend-dropdown class="d-inline-block">
  ...
</div>

stackblitz

受到 @Eliseo 答案的启发,到目前为止 ngbDropdown 该指令有一个名为 openChange 的方法将告诉布尔值的打开状态基我们可以创建一个新事件,如打开和关闭,并发出打开状态的值基础

ExtendDropdownDirective

@Directive({
  selector: "[extend-dropdown]"
})
export class ExtendDropdownDirective {
  @Output() open: EventEmitter<any> = new EventEmitter();
  @Output() close: EventEmitter<any> = new EventEmitter();

  @HostListener("openChange", ["$event"]) openChangeHandler(state: boolean) {
    if (state) {
      this.open.emit();
    } else {
      this.close.emit();
    }
  }
}

现在我们可以像这样使用它了

div ngbDropdown extend-dropdown class="d-inline-block" (open)="open()" (close)="close()">
        ...
</div>

demo