File

src/lib/navigation.service.ts

Index

Properties
Methods

Constructor

constructor(navigation: any, inserts: any | null)
Parameters :
Name Type Optional
navigation any No
inserts any | null No

Methods

Public add
add(id: string, value: NavigationWithInserts, update)
Parameters :
Name Type Optional Default value
id string No
value NavigationWithInserts No
update No true
Returns : void
Public get
get(id: string)
Parameters :
Name Type Optional
id string No
Returns : NavigationWithInserts | undefined
Public has
has(id: string)
Parameters :
Name Type Optional
id string No
Returns : boolean
Public insert
use add instead
insert(id: string, value: NavigationWithInserts, update)
Parameters :
Name Type Optional Default value
id string No
value NavigationWithInserts No
update No true
Returns : void
Public remove
remove(id: string, update)
Parameters :
Name Type Optional Default value
id string No
update No true
Returns : void
Public updateNavigation
updateNavigation()
Returns : void

Properties

Public Readonly config$
Type : Observable<Navigation>
import {
  inject,
  Inject,
  Injectable,
  Injector,
  INJECTOR,
  Optional,
} from '@angular/core';
import {
  combineLatest,
  from,
  Observable,
  of,
  ReplaySubject,
} from 'rxjs';
import {
  catchError,
  map,
  switchMap,
} from 'rxjs/operators';
import {
  IsNavigationDividerItem,
  IsNavigationInsertItem,
  IsNavigationItem,
  Navigation,
  NavigationDividerItem,
  NavigationItem,
  NavigationWithInserts,
} from './navigation/navigation-item';
import {
  RXAP_NAVIGATION_CONFIG,
  RXAP_NAVIGATION_CONFIG_INSERTS,
} from './tokens';

@Injectable()
export class NavigationService {
  public readonly config$: Observable<Navigation>;

  private inserts = new Map<string, NavigationWithInserts>();

  private readonly navigation: NavigationWithInserts;

  private readonly navigation$ = new ReplaySubject<Navigation>(1);

  private readonly injector: Injector = inject(INJECTOR);

  constructor(
    @Inject(RXAP_NAVIGATION_CONFIG)
      navigation: any,
    @Optional()
    @Inject(RXAP_NAVIGATION_CONFIG_INSERTS)
      inserts: any | null = null,
  ) {
    if (typeof navigation === 'function') {
      this.navigation = navigation();
    } else {
      this.navigation = navigation;
    }
    if (inserts) {
      Object.entries(inserts).forEach(([ id, insert ]: [ string, any ]) =>
        this.insert(id, insert, false),
      );
    }
    this.updateNavigation();
    this.config$ = this.navigation$.pipe(
      switchMap((navigationWithoutStatusCheck) =>
        this.checkNavigationStatusProviders(navigationWithoutStatusCheck),
      ),
    );
  }

  /**
   * @deprecated use add instead
   * @param id
   * @param value
   * @param update
   */
  public insert(
    id: string,
    value: NavigationWithInserts,
    update = true,
  ): void {
    this.add(id, value, update);
  }

  public add(
    id: string,
    value: NavigationWithInserts,
    update = true,
  ): void {
    this.inserts.set(id, value);
    if (update) {
      this.updateNavigation();
    }
  }

  public has(id: string): boolean {
    return this.inserts.has(id);
  }

  public get(id: string): NavigationWithInserts | undefined {
    return this.inserts.get(id);
  }

  public remove(id: string, update = true): void {
    this.inserts.delete(id);
    if (update) {
      this.updateNavigation();
    }
  }

  public updateNavigation(): void {
    this.navigation$.next(this.replaceInserts(this.navigation));
  }

  /**
   * @internal
   * @param navigationItem
   */
  public checkNavigationItemStatusProviders(
    navigationItem: NavigationItem | NavigationDividerItem,
  ): Observable<NavigationItem | NavigationDividerItem | null> {
    if (IsNavigationDividerItem(navigationItem) || !navigationItem.status) {
      return of(navigationItem);
    }
    const isVisibleArray$: Array<Observable<boolean>> = navigationItem
      .status
      .map((statusToken) => this.injector.get(
        statusToken))
      .map((status) => {
        const isVisible = status.isVisible(
          navigationItem);
        if (typeof isVisible === 'boolean') {
          return of(isVisible);
        } else {
          return from(isVisible);
        }
      })
      .map(isVisible$ => isVisible$.pipe(catchError(e => {
        console.error(`isVisible method failed: ${ e.message }`);
        return of(false);
      })));
    // TODO : dont wait for all status services to complete, but cancel waiting if one returns false
    return combineLatest(isVisibleArray$).pipe(
      map((isVisibleArray) =>
        isVisibleArray.reduce((acc, isVisible) => acc && isVisible, true),
      ),
      map((isVisible) => (isVisible ? navigationItem : null)),
      switchMap((navigationItemOrNull) => {
        if (navigationItemOrNull) {
          if (navigationItemOrNull.children?.length) {
            return this.checkNavigationStatusProviders(
              navigationItemOrNull.children,
            ).pipe(
              map((children) => ({
                ...navigationItemOrNull,
                children,
              })),
            );
          }
          return of(navigationItemOrNull);
        }
        return of(null);
      }),
    );
  }

  /**
   * @internal
   * @param navigationItem
   */
  public checkNavigationStatusProviders(
    navigation: Navigation,
  ): Observable<Navigation> {
    return combineLatest(
      navigation.map((navigationItem) =>
        this.checkNavigationItemStatusProviders(navigationItem),
      ),
    ).pipe(
      map((navigationWithNullItems) => {
        const cleanNavigation: Navigation = [];

        for (const navigationItem of navigationWithNullItems) {
          if (navigationItem !== null) {
            cleanNavigation.push(navigationItem);
          }
        }

        return cleanNavigation;
      }),
    );
  }

  private replaceInserts(
    navigationWithInserts: NavigationWithInserts,
  ): Navigation {
    const navigation: Navigation = [];

    for (const navigationItem of navigationWithInserts) {
      if (IsNavigationInsertItem(navigationItem)) {
        if (this.inserts.has(navigationItem.insert)) {
          navigation.push(
            ...this.replaceInserts(this.inserts.get(navigationItem.insert)!),
          );
        }
      } else if (IsNavigationItem(navigationItem)) {
        navigation.push({
          ...navigationItem,
          children: this.replaceInserts(navigationItem.children ?? []),
        });
      } else if (IsNavigationDividerItem(navigationItem)) {
        navigation.push(navigationItem);
      }
    }

    return navigation;
  }
}

results matching ""

    No results matching ""