typescript - Hur löser jag denna Angular 7 "Cirkulärt beroende upptäckt" varning


I mitt Angular 7-projekt vill jag ha en fil "pages.ts" där jag exporterar de rutter som min app kommer att ha. Dessa importeras av "app-routing.ts" för att användas av RouterModule, och importeras även av "pages.service.ts" där jag vill hantera datan för dessa rutter. Nu, i en av mina komponenter "test-one.component.ts" vill jag använda den tjänsten "pages.service.ts" så att jag till exempel kan hämta datan för en rutt via dess path eller titel. Allt fungerar korrekt men denna varning visas.

VARNING: Cirkulärt beroende upptäckt: src/app/pages/test-one/test-one.component.ts -> src/app/core/services/pages.service.ts -> src/app/core/constants/pages.ts -> src/app/pages/test-one/test-one.component.ts

"pages.ts"

import { Page } from '../../core/models/page.model';
import { LoginComponent } from '../../pages/login/login.component';
import { SignupComponent } from '../../pages/signup/signup.component';

export const routePages: Page[] = [
    {
        path: '',
        redirectTo: 'login',
        pathMatch: 'full'
    },
    {
        path: 'login',
        component: LoginComponent,
        canActivate: [],
        data: {
            title: 'Login',
            description: '',
            keywords: '',
            icon: 'fingerprint',
            hasBreadcrumb: true
        },
        pathMatch: 'full'
    },
    {
        path: 'sign-up',
        component: SignupComponent,
        canActivate: [],
        data: {
            title: 'Sign up',
            description: '',
            keywords: '',
            hasBreadcrumb: true
        },
        pathMatch: 'full'
    }
];

"app-routing.module.ts"

import { routePages } from './core/constants/pages';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';

@NgModule({
  imports: [RouterModule.forRoot(routePages, { scrollPositionRestoration: 'enabled' })],
  exports: [RouterModule]
})
export class AppRoutingModule { }

"pages.service.ts"

import { routePages } from './../constants/pages';
import { Page, PageData } from './../models/page.model';
import { Router } from '@angular/router';
import { Injectable, EventEmitter } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class PagesService {
  pages = routePages;
  private pagesChanged: EventEmitter<Page[]> = new EventEmitter<Page[]>();

  constructor(private location: Location, private router: Router) {
    this.setPagesFullPath();
    console.log(this.pages);
  }
.
.
.

}

"test-one.component.ts"

import { Page, PageData } from './../../core/models/page.model';
import { PagesService } from './../../core/services/pages.service';
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-test-one',
  templateUrl: './test-one.component.html',
  styleUrls: ['./test-one.component.scss']
})
export class TestOneComponent implements OnInit {
  testATwoPage: Page;

  constructor(private pagesService: PagesService) {
  }

  ngOnInit() {
    this.testATwoPage = this.pagesService.getPageByTitle('Test A two');
  }

}

Jag skulle gärna vilja veta hur jag kan lösa detta cirkulära beroende utan att behöva avstå från någon funktionalitet.

Tack i förväg!

Svar #1

Det finns inget direkt sätt runt det. Jag förstår dock ditt användningsfall och stöder det på ett sätt. Du borde inte ha den här typen av logik i din komponent. Det borde göras i en tjänst. Du har rätt om det. Jag är inte helt säker på att det är den bästa idén att återanvända Routes-exporten. Det känns som att du har det bättre av att använda Router.config-egenskapen. Den borde innehålla hela routningskonfigurationen:

@Injectable({
  providedIn: 'root'
})
export class PagesService {
  readonly pages: Routes = this.router.config;
  private pagesChanged: EventEmitter<Page[]> = new EventEmitter<Page[]>();

  constructor(private location: Location, private router: Router) {
    this.setPagesFullPath();
    console.log(this.pages);
  }
}

Om detta, av vilken anledning som helst, inte fungerar. Sättet att bli av med cirkulära beroenden är genom att dela upp i flera filer. Huvudproblemet är din routePages. Du kan konvertera detta till två filer. En av dem är pages.ts:

pages.ts

export enum PageRoute {
  Login: 'login',
  SignUp: 'sign-up'
}

export interface PageData {
  title: string;
  description: string;
  keywords: string;
  icon?: string;
  hasBreadcrumb: boolean;
}

export const PageRouteData: Record<PageRoute, PageData> {
  [AppPage.Login]: {
    title: 'Login',
    description: '',
    keywords: '',
    icon: 'fingerprint',
    hasBreadcrumb: true
  },
  [AppPage.SignUp]: {
    title: 'Sign up',
    description: '',
    keywords: '',
    hasBreadcrumb: true
  }
}

Och en annan fil som faktiskt innehåller dina rutter som tas om hand av routern:

app.routes.ts

export const routePages: Page[] = [
  {
      path: '',
      redirectTo: PageRoute.Login,
      pathMatch: 'full'
  },
  {
      path: PageRoute.Login,
      component: LoginComponent,
      data: PageRouteData[PageRoute.Login]
  },
  {
      path: PageRoute.SignUp,
      component: SignupComponent,
      data: PageRouteData[PageRoute.SignUp]
  }
];

Sidnot: pathMatch är endast nödvändigt om du har en redirectTo

På detta sätt behöver du inte importera app.routes i din PagesService och den cirkulära beroendet löses

Svar #2

Jag vet inte vad syftet med PageService är men jag tror att det är fel. För att komma åt routens data bör du använda Router-händelser eller ActivatedRoute och sedan göra vad du behöver göra. Till exempel i den här kodbiten hämtar jag informationen från routen för att använda i brödsmulor:

this.router.events
      .pipe(
        filter(event => event instanceof NavigationEnd),
        map(() => {
          return this.getFirstChild(this.route.firstChild);
        })
      )
      .subscribe(child => {
        const title = child.snapshot.data['title'];
        const previous = child.snapshot.data['previous'];
        const parentUrl = child?.snapshot.parent?.url;
        ...
      })

Relaterade artiklar