Opcjonalny parametr trasy kątowej 2

180

Czy można mieć opcjonalny parametr trasy w trasie Angular 2? Wypróbowałem składnię Angular 1.x w RouteConfig, ale otrzymałem poniższy błąd:

„ORYGINALNY WYJĄTEK: ścieżka” / user /: id? ”Zawiera znak„? ”, Który nie jest dozwolony w konfiguracji trasy.”

@RouteConfig([
{
    path: '/user/:id?',
    component: User,
    as: 'User'
}])
Jeroen
źródło

Odpowiedzi:

298

Możesz zdefiniować wiele tras z parametrem i bez:

@RouteConfig([
    { path: '/user/:id', component: User, name: 'User' },
    { path: '/user', component: User, name: 'Usernew' }
])

i obsłuż opcjonalny parametr w swoim komponencie:

constructor(params: RouteParams) {
    var paramId = params.get("id");

    if (paramId) {
        ...
    }
}

Zobacz także powiązany problem z githubem: https://github.com/angular/angular/issues/3525

rerezz
źródło
11
Popraw mnie, jeśli się mylę, ale to rozwiązanie działało u mnie tylko wtedy, gdy kolejność tras w tablicy była odwrócona, czyli trasa z parametrem wystąpiła przed drugą. Dopóki tego nie zrobiłem, router dopasował tylko trasę bez parametru.
Aviad P.
10
czy to rozwiązanie nadal ma zastosowanie? Zauważyłem, że przejście z „User” do „UserNew” spowoduje ponowne uruchomienie komponentu „User”
teleaziz
19
stary, ale poważny problem z tym podejściem polega na tym, że każda trasa jest traktowana jako unikalna trasa, co uniemożliwia ponowne wykorzystanie komponentów.
Agonia
4
Jak zauważył @teleaziz, dołączenie parametru spowoduje ponowne wyrenderowanie komponentu. Aby tego uniknąć, odpowiedź Martina Cremera; dodanie katalogu głównego „redirectTo” z pustą wartością parametru, działało świetnie: stackoverflow.com/a/49159166/1364650 - ale to dość hakerskie, myślę, że powinny po prostu poprawnie obsługiwać opcjonalne parametry trasy.
Vincent Sels
2
Dla tych, którzy zastanawiają się, dlaczego RouteParamsnie importować do komponentu, sprawdź to: stackoverflow.com/a/36792261/806202 . Rozwiązaniem jest użycie ActivatedRoute:route.snapshot.params['routeParam']
Arsen Khachaturyan
89
{path: 'users', redirectTo: 'users/', pathMatch: 'full'},
{path: 'users/:userId', component: UserComponent}

W ten sposób komponent nie jest ponownie renderowany po dodaniu parametru.

Martin Cremer
źródło
6
Ta odpowiedź jest najlepsza. Nie renderuje ponownie tego samego komponentu i nie wymaga wielu komponentów.
Rex
4
Najlepsza odpowiedź, ale dodałem pathMatch: 'full'do przekierowania, w przeciwnym razie ścieżki takie jak users/adminsą również przekierowane w moim przypadku
Valeriy Katkov
4
Ta odpowiedź jest najlepsza tylko wtedy, gdy nie przeszkadza Ci stosowanie końcowych ukośników w adresach URL wyświetlanych w przeglądarce. Rozważ może wartość, która reprezentuje „niezdefiniowany identyfikator”, na przykład /users/alllub /users/homeprzeczytaj „wszystko” lub „dom” jako idi po prostu zignoruj, jeśli pasuje do twojej magicznej wartości. Wtedy pierwsza linia powyżej stanie się redirectTo: 'users/home'lub cokolwiek zdecydujesz. Dla mnie ukośnik naprawdę wyróżnia się jako coś złego.
Simon_Weaver,
@Simon_Weaver Zgadzam się. Znalazłem inne rozwiązanie za pomocą dopasowującego, który nie ma tego problemu: stackoverflow.com/a/56391974/664533
Wayne Maurer
1
to proste zaklęcie, ale całkiem niezniszczalne: D Najlepsze rozwiązanie!
Verri,
45

Zaleca się użycie parametru zapytania, gdy informacje są opcjonalne.

Parametry trasy czy parametry zapytania?

Nie ma sztywnej reguły. Ogólnie,

wolisz parametr trasy, kiedy

  • wartość jest wymagana.
  • wartość jest niezbędna do odróżnienia jednej ścieżki od drugiej.

preferuj parametr zapytania, kiedy

  • wartość jest opcjonalna.
  • wartość jest złożona i / lub wielozmienna.

z https://angular.io/guide/router#optional-route-parameters

Wystarczy wyjąć parametr ze ścieżki trasy.

@RouteConfig([
{
    path: '/user/',
    component: User,
    as: 'User'
}])
Jp_
źródło
6
Zmiana opcjonalnych parametrów trasy powoduje ponowne renderowanie komponentów, ale zmiana queryParams nie. Również jeśli rozstrzygniesz jakieś dane przed nawigacją po trasie, będą one wymagane za każdym razem, gdy zmienisz opcjonalne parametry trasy.
Rakhat
1
FYI, ten link kotwicy już nie działa. Wygląda na to, że nowe łącze to Parametry trasy: wymagane czy opcjonalne?
spottedmahn
20

Kątowe 4 - Rozwiązanie dotyczące zamawiania opcjonalnego parametru:

ZRÓB TO:

const appRoutes: Routes = [
  {path: '', component: HomeComponent},
  {path: 'products', component: ProductsComponent},
  {path: 'products/:id', component: ProductsComponent}
]

Zwróć uwagę, że trasy productsi products/:idmają dokładnie takie same nazwy. Angular 4 będzie poprawnie podążał productsza trasami bez parametru, a jeśli parametr będzie podążał products/:id.

Jednak ścieżka dla trasy productsnieparametrowej nie może mieć końcowego ukośnika, w przeciwnym razie angular niepoprawnie potraktuje ją jako ścieżkę parametru. Więc w moim przypadku miałem końcowy ukośnik dla produktów i nie działał.

NIE ROBIĆ TEGO:

...
{path: 'products/', component: ProductsComponent},
{path: 'products/:id', component: ProductsComponent},
...
ObjectiveTC
źródło
Jeśli oba są kierowane do komponentu ProductsComponent, jak sobie radzisz z opcjonalnym parametrem tam?
Arwin,
1
Możesz uzyskać dostęp do parametrów: id1,: id2 itp., A także do żądanego adresu URL w ProductsComponent, na przykład this.route.url.first () .mergeMap ((url) => {// console.log ('1: wykryto zmianę adresu URL '+ url); return this.route.params.do ((params) => {// console.log (' 2: url + wykryto zmianę parametrów '+ params ["id1"] +' '+ params ["id2"]); this.id1 = params ["id1"]; this.id2 = params ["id2"];})})
ObjectiveTC
2
Pamiętaj, że możesz również przejść datado komponentu, który może być inny dla każdej trasy, nawet do tego samego komponentu. Przykład {path: 'products', component: ProductsComponent, data: { showAllProducts: true} },może zostać użyty, a następnie sprawdź showAllProducts. Trochę ładniej niż sprawdzanie wartości null, ale w prostszych przypadkach jedno i drugie prawdopodobnie jest w porządku.
Simon_Weaver,
1
Niestety, to rozwiązanie uniemożliwi Angularowi ponowne wykorzystanie komponentu między produktami a produktami /: id. Komponent zostanie ponownie zainwestowany.
Kodiak
@Kodiak - nie uważam, że to prawda. Rozumiem, że w app.module.ts instancja ProductsComponent jest tworzona raz, a silnik kątowy następnie ponownie używa tego komponentu ProductsComponent po każdym zdarzeniu umożliwiającym nawigację (produkty i produkty /: id itp.). Czy możesz wyjaśnić lub zademonstrować, w jaki sposób ProductsComponent może ponownie tworzyć instancję w powyższym kodzie i jak zapobiegać ponownej instancji?
ObjectiveTC
11

odpowiedź rerezz jest całkiem niezła, ale ma jedną poważną wadę. Powoduje Userto ponowne uruchomienie ngOnInitmetody przez komponent .

Może to być problematyczne, gdy robisz tam ciężkie rzeczy i nie chcesz, aby były ponownie uruchamiane, gdy przełączasz się z trasy nieparametrycznej na parametryczną. Chociaż te dwie trasy mają imitować opcjonalny parametr adresu URL, a nie stać się dwoma oddzielnymi trasami.

Oto, co proponuję, aby rozwiązać problem:

const routes = [
  {
    path: '/user',
    component: User,
    children: [
      { path: ':id', component: UserWithParam, name: 'Usernew' }
    ]
  }
];

Następnie możesz przenieść logikę odpowiedzialną za obsługę parametru do UserWithParamkomponentu i pozostawić logikę podstawową w Userkomponencie. Cokolwiek zrobisz User::ngOnInit, nie zostanie uruchomione ponownie, gdy przejdziesz z / user do / user / 123 .

Nie zapomnij umieścić znaku <router-outlet></router-outlet>w Userszablonie.

matewka
źródło
Unikanie odtwarzanego komponentu jest dobrą rzeczą, jeśli wydajność ma kluczowe znaczenie. Mam inne rozwiązanie, które również pozwala uniknąć ponownego tworzenia komponentu: stackoverflow.com/a/56391974/664533
Wayne Maurer
4

Sugerowane odpowiedzi tutaj, w tym zaakceptowana odpowiedź z ponownego odtworzenia, która sugeruje dodanie wielu wpisów trasy, działają dobrze.

Jednak komponent zostanie odtworzony podczas zmiany między wpisami trasy, tj. Między wpisem trasy z parametrem a wpisem bez parametru.

Jeśli chcesz tego uniknąć, możesz utworzyć własny element dopasowujący trasę, który będzie pasował do obu tras:

export function userPageMatcher(segments: UrlSegment[]): UrlMatchResult {
    if (segments.length > 0 && segments[0].path === 'user') {
        if (segments.length === 1) {
            return {
                consumed: segments,
                posParams: {},
            };
        }
        if (segments.length === 2) {
            return {
                consumed: segments,
                posParams: { id: segments[1] },
            };
        }
        return <UrlMatchResult>(null as any);
    }
    return <UrlMatchResult>(null as any);
 }

Następnie użyj dopasowania w konfiguracji trasy:

const routes: Routes = [
    {
        matcher: userPageMatcher,
        component: User,
    }
];
Wayne Maurer
źródło
@KevinBeal Zaimplementowałem sporo dopasowań, które działają z AOT. Jaki błąd tutaj otrzymujesz?
Wayne Maurer
Ups. To było coś innego. Mój matcher współpracuje z AOT.
Kevin Beal
jest to trochę trudne, ale najlepsze rozwiązanie tego problemu
fedor.belov
4

W angular4 musimy po prostu zorganizować trasy razem w hierarchii

const appRoutes: Routes = [
  { 
    path: '', 
    component: MainPageComponent 
  },
  { 
    path: 'car/details', 
    component: CarDetailsComponent 
  },
  { 
    path: 'car/details/platforms-products', 
    component: CarProductsComponent 
  },
  { 
    path: 'car/details/:id', 
    component: CadDetailsComponent 
  },
  { 
    path: 'car/details/:id/platforms-products', 
    component: CarProductsComponent 
  }
];

To działa dla mnie. W ten sposób router wie, jaka jest następna trasa na podstawie parametrów id opcji.

Ravi Jadhav
źródło
1

Wpadłem na inny przykład tego problemu i szukając rozwiązania przyszedł tutaj. Mój problem polegał na tym, że robiłem dzieci i leniwe ładowanie komponentów, aby trochę zoptymalizować. Krótko mówiąc, jeśli jesteś leniwy podczas ładowania modułu nadrzędnego. Najważniejsze było to, że użyłem „/: id” w trasie i narzekałem, że „/” jest jej częścią. Nie jest to dokładny problem, ale dotyczy.

Routing aplikacji od rodzica

...
const routes: Routes = [
  {
    path: '',
    children: [
      {
        path: 'pathOne',
        loadChildren: 'app/views/$MODULE_PATH.module#PathOneModule'
      },
      {
        path: 'pathTwo',
        loadChildren: 'app/views/$MODULE_PATH.module#PathTwoModule'
      },
...

Trasy podrzędne są ładowane z opóźnieniem

...
const routes: Routes = [
  {
    path: '',
    children: [
      {
        path: '',
        component: OverviewComponent
      },
      {
        path: ':id',
        component: DetailedComponent
      },
    ]
  }
];
...
LP
źródło
0

W obliczu podobnego problemu z leniwym ładowaniem zrobiłem to:

const routes: Routes = [
  {
    path: 'users',
    redirectTo: 'users/',
    pathMatch: 'full'
  },
  {
    path: 'users',
    loadChildren: './users/users.module#UserssModule',
    runGuardsAndResolvers: 'always'
  },
[...]

A potem w komponencie:

  ngOnInit() {
    this.activatedRoute.paramMap.pipe(
      switchMap(
        (params: ParamMap) => {
          let id: string = params.get('id');
          if (id == "") {
            return of(undefined);
          }
          return this.usersService.getUser(Number(params.get('id')));
        }
      )
    ).subscribe(user => this.selectedUser = user);
  }

Tą drogą:

  • Trasa bez /jest przekierowywana na trasę z. Z tego powodu pathMatch: 'full'przekierowywana jest tylko taka konkretna pełna trasa.

  • Następnie users/:idjest odbierany. Jeśli rzeczywista trasa była users/, idjest "", sprawdź ją ngOnIniti postępuj zgodnie z nią; w przeciwnym razie idjest id i kontynuuj.

  • Reszta składnika, na który działa, selectedUserjest lub nie jest nieokreślona (* ngIf i tym podobne).

Javier Sedano
źródło