Paramètre d'itinéraire facultatif angulaire 2

180

Est-il possible d'avoir un paramètre d'itinéraire facultatif dans l'itinéraire Angular 2? J'ai essayé la syntaxe Angular 1.x dans RouteConfig mais j'ai reçu l'erreur ci-dessous:

"EXCEPTION ORIGINALE: Chemin" / user /: id? "Contient"? "Qui n'est pas autorisé dans une configuration de route."

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

Réponses:

298

Vous pouvez définir plusieurs itinéraires avec et sans paramètre:

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

et gérez le paramètre facultatif dans votre composant:

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

    if (paramId) {
        ...
    }
}

Voir également le problème github associé: https://github.com/angular/angular/issues/3525

réveiller
la source
11
Corrigez-moi si je me trompe, mais cette solution ne fonctionnait pour moi que lorsque l'ordre des routes dans le tableau était inversé, c'est-à-dire que la route avec le paramètre se produisait avant l'autre. Jusqu'à ce que je fasse cela, le routeur ne correspondait qu'à l'itinéraire sans le paramètre.
Aviad P.
10
cette solution s'applique-t-elle toujours? J'ai remarqué que le passage de "User" à "UserNew" ré-instanciera le composant "User"
teleaziz le
19
ancien mais un problème majeur avec cette approche est que chaque route est traitée comme une route unique et rend la réutilisation des composants impossible.
Agony
4
Comme indiqué par @teleaziz, l'ajout du paramètre rendra le composant à nouveau. Pour éviter cela, la réponse de Martin Cremer; l'ajout d'une racine 'redirectTo' avec une valeur de paramètre vide, a très bien fonctionné pour moi: stackoverflow.com/a/49159166/1364650 - mais c'est assez hacky, je pense qu'ils devraient juste prendre en charge les paramètres de route optionnels correctement.
Vincent Sels
2
Pour ceux qui se demandent pourquoi RouteParamsne pas importer dans un composant, vérifiez ceci: stackoverflow.com/a/36792261/806202 . La solution est d'utiliser ActivatedRoute:route.snapshot.params['routeParam']
Arsen Khachaturyan
89
{path: 'users', redirectTo: 'users/', pathMatch: 'full'},
{path: 'users/:userId', component: UserComponent}

De cette façon, le composant n'est pas restitué lorsque le paramètre est ajouté.

Martin Cremer
la source
6
Cette réponse est la meilleure. Il ne restitue pas le même composant et ne nécessite pas plusieurs composants.
Rex
4
La meilleure réponse, mais j'ai ajouté pathMatch: 'full'pour rediriger, sinon des chemins comme users/adminsont également redirigés dans mon cas
Valeriy Katkov
4
Cette réponse n'est la meilleure que si vous êtes d'accord avec les barres obliques de fin sur vos URL telles qu'elles sont affichées dans le navigateur. Considérez peut-être une valeur qui représente «un identifiant non défini», par exemple /users/allou /users/home, lisez «tous» ou «accueil» comme le idet ignorez-le simplement s'il correspond à votre valeur magique. Ensuite, la première ligne ci-dessus devient redirectTo: 'users/home'ou ce que vous décidez. Pour moi, une barre oblique à la fin se démarque vraiment comme quelque chose qui ne va pas.
Simon_Weaver
@Simon_Weaver Je suis d'accord. J'ai trouvé une autre solution en utilisant un matcher qui n'a pas ce problème: stackoverflow.com/a/56391974/664533
Wayne Maurer
1
c'est un sort simple mais tout à fait incassable: D La meilleure solution!
Verri
45

Il est recommandé d'utiliser un paramètre de requête lorsque les informations sont facultatives.

Paramètres d'itinéraire ou paramètres de requête?

Il n'y a pas de règle absolue. En général,

préférez un paramètre d'itinéraire lorsque

  • la valeur est obligatoire.
  • la valeur est nécessaire pour distinguer un chemin de route d'un autre.

préférez un paramètre de requête lorsque

  • la valeur est facultative.
  • la valeur est complexe et / ou multi-variée.

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

Il vous suffit de retirer le paramètre du chemin de la route.

@RouteConfig([
{
    path: '/user/',
    component: User,
    as: 'User'
}])
Jp_
la source
6
La modification des paramètres d'itinéraire facultatifs rend les composants, mais la modification de queryParams ne le fait pas. De plus, si vous résolvez certaines données avant la navigation d'itinéraire, elles seront demandées à chaque fois que vous modifiez les paramètres d'itinéraire facultatifs.
Rakhat
1
FYI, ce lien d'ancrage ne fonctionne plus. Le nouveau lien semble être Paramètres d'itinéraire: obligatoire ou facultatif?
spottedmahn
20

Angular 4 - Solution pour traiter la commande du paramètre optionnel:

FAITES CECI:

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

Notez que les routes productset products/:idsont nommées exactement de la même manière. Angular 4 suivra correctement productspour les routes sans paramètre, et si un paramètre il suivra products/:id.

Cependant, le chemin de la route sans paramètre neproducts doit pas avoir de barre oblique à la fin, sinon angular le traitera de manière incorrecte comme un chemin de paramètre. Donc, dans mon cas, j'avais la barre oblique finale pour les produits et cela ne fonctionnait pas.

NE FAITES PAS CELA:

...
{path: 'products/', component: ProductsComponent},
{path: 'products/:id', component: ProductsComponent},
...
ObjectifTC
la source
Si les deux vont au ProductsComponent, comment gérez-vous le paramètre facultatif?
Arwin
1
Vous pouvez accéder aux paramètres: id1,: id2, etc. ainsi qu'à l'url demandée dans ProductsComponent comme ceci: this.route.url.first () .mergeMap ((url) => {// console.log ('1: changement d'url détecté '+ url); renvoyer this.route.params.do ((params) => {// console.log (' 2: changement d'url + paramètres détecté '+ params ["id1"] +' '+ paramètres ["id2"]); this.id1 = params ["id1"]; this.id2 = params ["id2"];})})
ObjectiveTC
2
N'oubliez pas que vous pouvez également passer dataau composant, qui peut être différent pour chaque route, même vers le même composant. Un exemple {path: 'products', component: ProductsComponent, data: { showAllProducts: true} },peut être utilisé et ensuite vous vérifiez showAllProducts. Un peu plus agréable que de vérifier un null, mais pour les cas plus simples, c'est probablement bien.
Simon_Weaver
1
Malheureusement, cette solution empêchera Angular de réutiliser le composant entre les produits et les produits /: id. Le composant sera réinstancié.
Kodiak
@Kodiak - Je ne pense pas que ce soit correct. Je crois comprendre que dans app.module.ts, le ProductsComponent est instancié une fois, et que le moteur angulaire réutilise ensuite ce ProductsComponent instancié à chaque événement navigable (produits et produits /: id, etc.). Pouvez-vous expliquer ou démontrer comment ProductsComponent pourrait être ré-instancié dans le code ci-dessus, et comment vous feriez pour empêcher la ré-instanciation?
ObjectiveTC
11

La réponse de rerezz est plutôt sympa mais elle a un grave défaut. Cela oblige le Usercomposant à réexécuter la ngOnInitméthode.

Cela peut être problématique lorsque vous faites des choses lourdes là-bas et que vous ne voulez pas qu'il soit réexécuté lorsque vous passez de la route non paramétrique à la route paramétrique. Bien que ces deux routes soient destinées à imiter un paramètre d'url facultatif, elles ne deviennent pas 2 routes distinctes.

Voici ce que je suggère pour résoudre le problème:

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

Vous pouvez ensuite déplacer la logique responsable de la gestion du paramètre vers le UserWithParamcomposant et laisser la logique de base dans le Usercomposant. Tout ce que vous faites ne User::ngOnInitsera pas exécuté à nouveau lorsque vous naviguerez de / user vers / user / 123 .

N'oubliez pas de mettre le <router-outlet></router-outlet>dans le Usermodèle de.

matewka
la source
Éviter la recréation du composant est une bonne chose si les performances sont essentielles. J'ai une autre solution qui évite aussi d'éviter la recréation du composant: stackoverflow.com/a/56391974/664533
Wayne Maurer
4

Les réponses suggérées ici, y compris la réponse acceptée de rerezz qui suggèrent d'ajouter plusieurs entrées d'itinéraire fonctionnent correctement .

Cependant, le composant sera recréé lors du passage entre les entrées de route, c'est-à-dire entre l'entrée de route avec le paramètre et l'entrée sans le paramètre.

Si vous voulez éviter cela, vous pouvez créer votre propre calculateur d'itinéraire qui correspondra aux deux itinéraires:

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

Ensuite, utilisez le matcher dans votre configuration d'itinéraire:

const routes: Routes = [
    {
        matcher: userPageMatcher,
        component: User,
    }
];
Wayne Maurer
la source
@KevinBeal J'ai implémenté pas mal de matchers qui fonctionnent avec AOT. Quelle est l'erreur que vous obtenez ici?
Wayne Maurer
Oups. C'était autre chose. Mon matcher fonctionne avec AOT.
Kevin Beal
c'est un peu délicat mais la meilleure solution à ce problème
fedor.belov
4

Avec angular4, nous avons juste besoin d'organiser les itinéraires ensemble dans la hiérarchie

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

Cela fonctionne pour moi. De cette façon, le routeur sait quelle est la prochaine route en fonction des paramètres d'ID d'option.

Ravi Jadhav
la source
1

Ran dans une autre instance de ce problème, et en cherchant une solution, est venu ici. Mon problème était que je faisais les enfants et le chargement paresseux des composants pour optimiser un peu les choses. En bref, si vous chargez paresseusement le module parent. Le principal était mon utilisation de «/: id» dans la route, et je me suis plaint de «/» en faisant partie. Ce n'est pas le problème exact ici, mais cela s'applique.

Routage de l'application depuis le parent

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

Routes enfants chargées paresseusement

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

Face à un problème similaire avec le chargement paresseux, j'ai fait ceci:

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

Et puis dans le composant:

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

Par ici:

  • L'itinéraire sans /est redirigé vers l'itinéraire avec. En raison de la pathMatch: 'full', seule cette route complète spécifique est redirigée.

  • Ensuite, users/:idest reçu. Si l'itinéraire réel était users/, idest "", alors enregistrez-le ngOnInitet agissez en conséquence; sinon, idest l'identifiant et continuez.

  • Le reste du composant agit sur selectedUserest ou non indéfini (* ngIf et les choses comme ça).

Javier Sedano
la source