Winkel 2 optionaler Routenparameter

180

Ist es möglich, einen optionalen Routenparameter in der Angular 2-Route zu haben? Ich habe die Angular 1.x-Syntax in RouteConfig ausprobiert, aber die folgende Fehlermeldung erhalten:

"ORIGINAL EXCEPTION: Path" / user /: id? "Enthält"? ", Was in einer Routenkonfiguration nicht zulässig ist."

@RouteConfig([
{
    path: '/user/:id?',
    component: User,
    as: 'User'
}])
Jeroen
quelle

Antworten:

296

Sie können mehrere Routen mit und ohne Parameter definieren:

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

und behandeln Sie den optionalen Parameter in Ihrer Komponente:

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

    if (paramId) {
        ...
    }
}

Siehe auch das zugehörige Github-Problem: https://github.com/angular/angular/issues/3525

rerezz
quelle
11
Korrigieren Sie mich, wenn ich falsch liege, aber diese Lösung hat bei mir nur funktioniert, wenn die Reihenfolge der Routen im Array umgekehrt wurde, dh die Route mit dem Parameter vor der anderen aufgetreten ist. Bis dahin stimmte der Router nur mit der Route ohne den Parameter überein.
Aviad P.
10
gilt diese Lösung noch? Ich habe festgestellt, dass beim Wechsel von "User" zu "UserNew" die "User" -Komponente
teleaziz
19
alt, aber ein Hauptproblem bei diesem Ansatz ist, dass jede Route als eindeutige Route behandelt wird und die Wiederverwendung von Komponenten unmöglich wird.
Qual
4
Wie von @teleaziz angegeben, wird die Komponente durch Anhängen des Parameters erneut gerendert. Um dies zu vermeiden, antwortete Martin Cremer; Das Hinzufügen eines 'redirectTo'- Stamms mit einem leeren Parameterwert hat für mich hervorragend funktioniert: stackoverflow.com/a/49159166/1364650 - aber das ist ziemlich hackig, ich denke, sie sollten nur optionale Routenparameter richtig unterstützen.
Vincent Sels
2
Wenn Sie sich fragen, warum Sie RouteParamsnicht in eine Komponente importieren sollten, überprüfen Sie dies: stackoverflow.com/a/36792261/806202 . Die Lösung ist zu verwenden ActivatedRoute:route.snapshot.params['routeParam']
Arsen Khachaturyan
89
{path: 'users', redirectTo: 'users/', pathMatch: 'full'},
{path: 'users/:userId', component: UserComponent}

Auf diese Weise wird die Komponente beim Hinzufügen des Parameters nicht erneut gerendert.

Martin Cremer
quelle
6
Diese Antwort ist die beste. Es wird nicht dieselbe Komponente erneut gerendert und es sind nicht mehrere Komponenten erforderlich.
Rex
4
Die beste Antwort, aber ich habe hinzugefügt, pathMatch: 'full'um umzuleiten, sonst werden Pfade wie users/adminauch in meinem Fall umgeleitet
Valeriy Katkov
4
Diese Antwort ist nur dann die beste, wenn Sie mit abschließenden Schrägstrichen in Ihren URLs einverstanden sind, wie sie im Browser angezeigt werden. Stellen Sie sich vielleicht einen Wert vor, der beispielsweise 'eine undefinierte ID' darstellt, /users/alloder /users/homelesen Sie 'all' oder 'home' als idund ignorieren Sie ihn einfach, wenn er Ihrem magischen Wert entspricht. Dann wird die erste Zeile oben redirectTo: 'users/home'oder was auch immer Sie entscheiden. Für mich ist ein abschließender Schrägstrich wirklich etwas, das nicht stimmt.
Simon_Weaver
@ Simon_Weaver Ich stimme zu. Ich habe eine andere Lösung mit einem Matcher gefunden, der dieses Problem nicht hat: stackoverflow.com/a/56391974/664533
Wayne Maurer
1
Es ist ein einfacher Zauber, aber ziemlich unzerbrechlich: D Die beste Lösung!
Verri
45

Es wird empfohlen, einen Abfrageparameter zu verwenden, wenn die Informationen optional sind.

Routenparameter oder Abfrageparameter?

Es gibt keine feste Regel. Allgemein,

bevorzugen Sie einen Routenparameter, wenn

  • Der Wert ist erforderlich.
  • Der Wert ist erforderlich, um einen Routenpfad von einem anderen zu unterscheiden.

bevorzugen Sie einen Abfrageparameter, wenn

  • Der Wert ist optional.
  • Der Wert ist komplex und / oder vielfältig.

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

Sie müssen nur den Parameter aus dem Routenpfad entfernen.

@RouteConfig([
{
    path: '/user/',
    component: User,
    as: 'User'
}])
Jp_
quelle
6
Durch Ändern optionaler Routenparameter werden Komponenten neu gerendert, durch Ändern von queryParams jedoch nicht. Wenn Sie einige Daten vor der Routennavigation auflösen, werden sie jedes Mal angefordert, wenn Sie optionale Routenparameter ändern.
Rakhat
1
Zu Ihrer Information, dieser Ankerlink funktioniert nicht mehr. Der neue Link scheint Routenparameter zu sein: Erforderlich oder optional?
Spottedmahn
20

Winkel 4 - Lösung zur Adressierung der Reihenfolge des optionalen Parameters:

MACH DAS:

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

Beachten Sie, dass die Routen productsund products/:idgenau gleich benannt sind. Winkel 4 folgt korrekt productsfür Routen ohne Parameter, und wenn ein Parameter folgtproducts/:id .

Der Pfad für die Nicht-Parameter-Route productsdarf jedoch keinen abschließenden Schrägstrich enthalten, da sonst Angular ihn fälschlicherweise als Parameterpfad behandelt. In meinem Fall hatte ich also den nachgestellten Schrägstrich für Produkte und es funktionierte nicht.

TUN SIE DAS NICHT:

...
{path: 'products/', component: ProductsComponent},
{path: 'products/:id', component: ProductsComponent},
...
ObjectiveTC
quelle
Wenn beide zur ProductsComponent gehen, wie gehen Sie dort mit dem optionalen Parameter um?
Arwin
1
Sie können auf die Parameter: id1 ,: id2 usw. sowie auf die angeforderte URL in ProductsComponent wie folgt zugreifen: this.route.url.first () .mergeMap ((url) => {// console.log ('1: URL-Änderung erkannt '+ url); return this.route.params.do ((params) => {// console.log (' 2: URL + Parameteränderung erkannt '+ params ["id1"] +' '+ params ["id2"]); this.id1 = params ["id1"]; this.id2 = params ["id2"];})})
ObjectiveTC
2
Denken Sie daran, dass Sie auch dataan die Komponente übergeben können, die für jede Route auch für dieselbe Komponente unterschiedlich sein kann. Beispiel {path: 'products', component: ProductsComponent, data: { showAllProducts: true} },könnte verwendet werden und dann suchen Sie nach showAllProducts. Ein bisschen schöner als nach einer Null zu suchen, aber für einfachere Fälle ist beides wahrscheinlich in Ordnung.
Simon_Weaver
1
Leider verhindert diese Lösung, dass Angular die Komponente zwischen Produkten und Produkten /: id wiederverwendet. Die Komponente wird erneut instanziiert.
Kodiak
@ Kodiak - Ich glaube nicht, dass das richtig ist. Ich verstehe, dass in app.module.ts die ProductsComponent einmal instanziiert wird und dass die Winkel-Engine diese instanziierte ProductsComponent bei jedem navigierbaren Ereignis (Produkte und Produkte /: id usw.) wiederverwendet. Können Sie im obigen Code erklären oder demonstrieren, wie ProductsComponent möglicherweise erneut instanziiert wird und wie Sie die erneute Instanziierung verhindern würden?
ObjectiveTC
11

Die Antwort von rerezz ist ziemlich nett, hat aber einen schwerwiegenden Fehler. Dadurch Userwird die ngOnInitMethode von der Komponente erneut ausgeführt .

Es kann problematisch sein, wenn Sie dort schwere Aufgaben ausführen und nicht möchten, dass diese erneut ausgeführt werden, wenn Sie von der nicht parametrischen zur parametrischen Route wechseln. Obwohl diese beiden Routen einen optionalen URL-Parameter imitieren sollen, werden sie nicht zu zwei separaten Routen.

Folgendes schlage ich vor, um das Problem zu lösen:

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

Dann können Sie die Logik verantwortlich für den Umgang mit dem param auf die bewegen UserWithParamKomponente und lassen Sie die Basis - Logik in der UserKomponente. Was auch immer Sie tun, User::ngOnInitwird nicht erneut ausgeführt, wenn Sie von / user zu / user / 123 navigieren .

Vergessen Sie nicht, das <router-outlet></router-outlet>in die UserVorlage des ' zu setzen .

Matewka
quelle
Das Vermeiden der Neuerstellung der Komponente ist eine gute Sache, wenn die Leistung kritisch ist. Ich habe eine andere Lösung, die auch vermeidet, dass die Komponente neu erstellt wird: stackoverflow.com/a/56391974/664533
Wayne Maurer
4

Die hier vorgeschlagenen Antworten, einschließlich der akzeptierten Antwort von rerezz das Hinzufügen mehrerer vorschlägt, funktionieren .

Die Komponente wird jedoch neu erstellt, wenn zwischen den Routeneinträgen gewechselt wird, dh zwischen dem Routeneintrag mit dem Parameter und dem Eintrag ohne Parameter.

Wenn Sie dies vermeiden möchten, können Sie einen eigenen Routen-Matcher erstellen, der beiden Routen entspricht:

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);
 }

Verwenden Sie dann den Matcher in Ihrer Routenkonfiguration:

const routes: Routes = [
    {
        matcher: userPageMatcher,
        component: User,
    }
];
Wayne Maurer
quelle
@ KevinBeal Ich habe einige Matcher implementiert, die mit AOT funktionieren. Was ist der Fehler, den Sie hier bekommen?
Wayne Maurer
Hoppla. Es war etwas anderes. Mein Matcher arbeitet mit AOT.
Kevin Beal
Das ist ein bisschen knifflig, aber die beste Lösung für dieses Problem
fedor.belov
4

Mit angle4 müssen wir nur Routen in Hierarchien zusammen organisieren

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 
  }
];

Das funktioniert bei mir. Auf diese Weise weiß der Router anhand der Options-ID-Parameter, welche Route die nächste ist.

Ravi Jadhav
quelle
1

Bin auf eine andere Instanz dieses Problems gestoßen und auf der Suche nach einer Lösung hierher gekommen. Mein Problem war, dass ich die Kinder machte und auch die Komponenten faul lud, um die Dinge ein wenig zu optimieren. Kurz gesagt, wenn Sie faul sind, laden Sie das übergeordnete Modul. Hauptsache, ich habe '/: id' in der Route verwendet und es gibt Beschwerden darüber, dass '/' ein Teil davon ist. Nicht das genaue Problem hier, aber es gilt.

App-Routing vom übergeordneten Element

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

Kinderrouten faul geladen

...
const routes: Routes = [
  {
    path: '',
    children: [
      {
        path: '',
        component: OverviewComponent
      },
      {
        path: ':id',
        component: DetailedComponent
      },
    ]
  }
];
...
LP
quelle
0

Bei einem ähnlichen Problem mit dem verzögerten Laden habe ich Folgendes getan:

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

Und dann in der Komponente:

  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);
  }

Diesen Weg:

  • Die Route ohne /wird auf die Route mit umgeleitet. Aufgrund dessen pathMatch: 'full'wird nur eine solche spezifische vollständige Route umgeleitet.

  • Dann users/:idwird empfangen. Wenn die tatsächliche Route war users/, idist "", checken Sie sie ein ngOnInitund handeln Sie entsprechend. Ansonsten idist die ID und fahren Sie fort.

  • Der Rest der Komponente, auf die einwirkt, selectedUserist undefiniert oder nicht (* ngIf und ähnliche Dinge).

Javier Sedano
quelle