11import { DOCUMENT } from '@angular/common' ;
22import {
3- AfterContentInit ,
43 AfterViewInit ,
54 booleanAttribute ,
65 ChangeDetectorRef ,
76 Component ,
87 computed ,
9- ContentChild ,
8+ contentChild ,
109 DestroyRef ,
1110 Directive ,
1211 effect ,
1312 ElementRef ,
1413 forwardRef ,
15- Inject ,
1614 inject ,
1715 input ,
1816 linkedSignal ,
@@ -24,7 +22,7 @@ import {
2422 signal ,
2523 untracked
2624} from '@angular/core' ;
27- import { Subscription } from 'rxjs' ;
25+ import { takeUntilDestroyed } from '@angular/core/ rxjs-interop ' ;
2826import { filter } from 'rxjs/operators' ;
2927
3028import { createPopper , Instance , Options , Placement } from '@popperjs/core' ;
@@ -128,15 +126,16 @@ export class DropdownToggleDirective implements AfterViewInit {
128126 '(click)' : 'onHostClick($event)'
129127 }
130128} )
131- export class DropdownComponent implements AfterContentInit , OnDestroy , OnInit {
132- constructor (
133- @Inject ( DOCUMENT ) private document : Document ,
134- private elementRef : ElementRef ,
135- private renderer : Renderer2 ,
136- private ngZone : NgZone ,
137- private changeDetectorRef : ChangeDetectorRef ,
138- public dropdownService : DropdownService
139- ) {
129+ export class DropdownComponent implements OnDestroy , OnInit {
130+ readonly #destroyRef = inject ( DestroyRef ) ;
131+ readonly #document = inject ( DOCUMENT ) ;
132+ readonly #elementRef = inject ( ElementRef ) ;
133+ readonly #renderer = inject ( Renderer2 ) ;
134+ readonly #ngZone = inject ( NgZone ) ;
135+ readonly #changeDetectorRef = inject ( ChangeDetectorRef ) ;
136+ readonly dropdownService = inject ( DropdownService ) ;
137+
138+ constructor ( ) {
140139 this . dropdownStateSubscribe ( ) ;
141140 }
142141
@@ -177,7 +176,7 @@ export class DropdownComponent implements AfterContentInit, OnDestroy, OnInit {
177176 */
178177 readonly popperOptionsInput = input < Partial < Options > > ( { } , { alias : 'popperOptions' } ) ;
179178
180- readonly popperOptionsEffect = effect ( ( ) => {
179+ readonly # popperOptionsEffect = effect ( ( ) => {
181180 this . popperOptions = { ...untracked ( this . #popperOptions) , ...this . popperOptionsInput ( ) } ;
182181 } ) ;
183182
@@ -239,7 +238,7 @@ export class DropdownComponent implements AfterContentInit, OnDestroy, OnInit {
239238 computation : ( value ) => value
240239 } ) ;
241240
242- readonly visibleEffect = effect ( ( ) => {
241+ readonly # visibleEffect = effect ( ( ) => {
243242 const visible = this . visible ( ) ;
244243 this . activeTrap = visible ;
245244 visible ? this . createPopperInstance ( ) : this . destroyPopperInstance ( ) ;
@@ -250,13 +249,12 @@ export class DropdownComponent implements AfterContentInit, OnDestroy, OnInit {
250249 readonly visibleChange = output < boolean > ( ) ;
251250
252251 dropdownContext = { $implicit : this . visible ( ) } ;
253- @ ContentChild ( DropdownToggleDirective ) _toggler ! : DropdownToggleDirective ;
254- @ ContentChild ( DropdownMenuDirective ) _menu ! : DropdownMenuDirective ;
255- @ ContentChild ( DropdownMenuDirective , { read : ElementRef } ) _menuElementRef ! : ElementRef ;
252+ readonly _toggler = contentChild ( DropdownToggleDirective ) ;
253+ readonly _menu = contentChild ( DropdownMenuDirective ) ;
254+ readonly _menuElementRef = contentChild ( DropdownMenuDirective , { read : ElementRef } ) ;
256255
257256 public activeTrap = false ;
258257
259- private dropdownStateSubscription ! : Subscription ;
260258 private popperInstance ! : Instance | undefined ;
261259 private listeners : ( ( ) => void ) [ ] = [ ] ;
262260
@@ -283,47 +281,45 @@ export class DropdownComponent implements AfterContentInit, OnDestroy, OnInit {
283281 this . clickedTarget = $event . target as HTMLElement ;
284282 }
285283
286- dropdownStateSubscribe ( subscribe : boolean = true ) : void {
287- if ( subscribe ) {
288- this . dropdownStateSubscription = this . dropdownService . dropdownState$
289- . pipe (
290- filter ( ( state ) => {
291- return this === state . dropdown ;
292- } )
293- )
294- . subscribe ( ( state ) => {
295- if ( 'visible' in state ) {
296- state ?. visible === 'toggle' ? this . toggleDropdown ( ) : this . visible . set ( state . visible ) ;
297- }
298- } ) ;
299- } else {
300- this . dropdownStateSubscription ?. unsubscribe ( ) ;
301- }
284+ dropdownStateSubscribe ( ) : void {
285+ this . dropdownService . dropdownState$
286+ . pipe (
287+ filter ( ( state ) => {
288+ return this === state . dropdown ;
289+ } ) ,
290+ takeUntilDestroyed ( this . #destroyRef)
291+ )
292+ . subscribe ( ( state ) => {
293+ if ( 'visible' in state ) {
294+ state ?. visible === 'toggle' ? this . toggleDropdown ( ) : this . visible . set ( state . visible ) ;
295+ }
296+ } ) ;
302297 }
303298
304299 toggleDropdown ( ) : void {
305300 this . visible . update ( ( visible ) => ! visible ) ;
306301 }
307302
308303 onClick ( event : any ) : void {
309- if ( ! this . _toggler ?. elementRef . nativeElement . contains ( event . target ?. closest ( '[cDropdownToggle]' ) ) ) {
304+ if ( ! this . _toggler ( ) ?. elementRef . nativeElement . contains ( event . target ?. closest ( '[cDropdownToggle]' ) ) ) {
310305 this . toggleDropdown ( ) ;
311306 }
312307 }
313308
314- ngAfterContentInit ( ) : void {
315- if ( this . variant ( ) === 'nav-item' ) {
316- this . renderer . addClass ( this . _toggler . elementRef . nativeElement , 'nav-link' ) ;
309+ readonly #togglerEffect = effect ( ( ) => {
310+ const variant = this . variant ( ) ;
311+ const _toggler = this . _toggler ( ) ;
312+ if ( variant === 'nav-item' && _toggler ) {
313+ this . #renderer. addClass ( _toggler . elementRef . nativeElement , 'nav-link' ) ;
317314 }
318- }
315+ } ) ;
319316
320317 ngOnInit ( ) : void {
321318 this . setVisibleState ( this . visible ( ) ) ;
322319 }
323320
324321 ngOnDestroy ( ) : void {
325322 this . clearListeners ( ) ;
326- this . dropdownStateSubscribe ( false ) ;
327323 this . destroyPopperInstance ( ) ;
328324 }
329325
@@ -333,22 +329,22 @@ export class DropdownComponent implements AfterContentInit, OnDestroy, OnInit {
333329
334330 // todo: turn off popper in navbar-nav
335331 createPopperInstance ( ) : void {
336- if ( this . _toggler && this . _menu ) {
337- this . ngZone . runOutsideAngular ( ( ) => {
332+ const _toggler = this . _toggler ( ) ;
333+ const _menu = this . _menu ( ) ;
334+ if ( _toggler && _menu ) {
335+ this . #ngZone. runOutsideAngular ( ( ) => {
338336 // workaround for popper position calculate (see also: dropdown-menu.component)
339- this . _menu . elementRef . nativeElement . style . visibility = 'hidden' ;
340- this . _menu . elementRef . nativeElement . style . display = 'block' ;
337+ _menu . elementRef . nativeElement . style . visibility = 'hidden' ;
338+ _menu . elementRef . nativeElement . style . display = 'block' ;
341339 if ( this . popper ( ) ) {
342- this . popperInstance = createPopper (
343- this . _toggler . elementRef . nativeElement ,
344- this . _menu . elementRef . nativeElement ,
345- { ...this . popperOptions }
346- ) ;
340+ this . popperInstance = createPopper ( _toggler . elementRef . nativeElement , _menu . elementRef . nativeElement , {
341+ ...this . popperOptions
342+ } ) ;
347343 }
348- this . ngZone . run ( ( ) => {
344+ this . # ngZone. run ( ( ) => {
349345 this . setListeners ( ) ;
350- this . changeDetectorRef . markForCheck ( ) ;
351- this . changeDetectorRef . detectChanges ( ) ;
346+ this . # changeDetectorRef. markForCheck ( ) ;
347+ this . # changeDetectorRef. detectChanges ( ) ;
352348 } ) ;
353349 } ) ;
354350 }
@@ -358,17 +354,17 @@ export class DropdownComponent implements AfterContentInit, OnDestroy, OnInit {
358354 this . clearListeners ( ) ;
359355 this . popperInstance ?. destroy ( ) ;
360356 this . popperInstance = undefined ;
361- this . changeDetectorRef . markForCheck ( ) ;
357+ this . # changeDetectorRef. markForCheck ( ) ;
362358 }
363359
364360 private setListeners ( ) : void {
365361 this . listeners . push (
366- this . renderer . listen ( this . document , 'click' , ( event ) => {
362+ this . # renderer. listen ( this . # document, 'click' , ( event ) => {
367363 const target = event . target as HTMLElement ;
368- if ( this . _menuElementRef ?. nativeElement . contains ( event . target ) ) {
364+ if ( this . _menuElementRef ( ) ?. nativeElement . contains ( event . target ) ) {
369365 this . clickedTarget = target ;
370366 }
371- if ( this . _toggler ?. elementRef . nativeElement . contains ( event . target ) ) {
367+ if ( this . _toggler ( ) ?. elementRef . nativeElement . contains ( event . target ) ) {
372368 return ;
373369 }
374370 const autoClose = this . autoClose ( ) ;
@@ -387,7 +383,7 @@ export class DropdownComponent implements AfterContentInit, OnDestroy, OnInit {
387383 } )
388384 ) ;
389385 this . listeners . push (
390- this . renderer . listen ( this . elementRef . nativeElement , 'keyup' , ( event ) => {
386+ this . # renderer. listen ( this . # elementRef. nativeElement , 'keyup' , ( event ) => {
391387 if ( event . key === 'Escape' && this . autoClose ( ) !== false ) {
392388 event . stopPropagation ( ) ;
393389 this . setVisibleState ( false ) ;
@@ -396,11 +392,11 @@ export class DropdownComponent implements AfterContentInit, OnDestroy, OnInit {
396392 } )
397393 ) ;
398394 this . listeners . push (
399- this . renderer . listen ( this . document , 'keyup' , ( event ) => {
395+ this . # renderer. listen ( this . # document, 'keyup' , ( event ) => {
400396 if (
401397 event . key === 'Tab' &&
402398 this . autoClose ( ) !== false &&
403- ! this . elementRef . nativeElement . contains ( event . target )
399+ ! this . # elementRef. nativeElement . contains ( event . target )
404400 ) {
405401 this . setVisibleState ( false ) ;
406402 return ;
0 commit comments