Parâmetro de rota opcional angular 2

180

É possível ter um parâmetro de rota opcional na rota Angular 2? Eu tentei a sintaxe Angular 1.x no RouteConfig, mas recebi o erro abaixo:

"EXCEÇÃO ORIGINAL: Caminho" / user /: id? "Contém"? ", O que não é permitido em uma configuração de rota."

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

Respostas:

298

Você pode definir várias rotas com e sem o parâmetro:

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

e manipule o parâmetro opcional em seu componente:

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

    if (paramId) {
        ...
    }
}

Consulte também a questão relacionada ao github: https://github.com/angular/angular/issues/3525

rerezz
fonte
11
Corrija-me se estiver errado, mas esta solução funcionou para mim somente quando a ordem das rotas na matriz foi revertida, ou seja, a rota com o parâmetro ocorreu antes da outra. Até eu fazer isso, o roteador correspondia apenas à rota sem o parâmetro
Aviad P.
10
essa solução ainda se aplica? Notei que se deslocam de "Usuário" para "UserNew" re-instanciar o 'User' componente
teleaziz
19
antigo, mas um grande problema com essa abordagem é que cada rota é tratada como uma rota única e torna impossível a reutilização de componentes.
Agonia
4
Conforme observado por @teleaziz, o acréscimo do parâmetro renderizará novamente o componente. Para evitar isso, a resposta de Martin Cremer; adicionar uma raiz 'redirectTo' com um valor de parâmetro em branco funcionou muito bem para mim: stackoverflow.com/a/49159166/1364650 - mas isso é bastante hacky, acho que eles devem apenas suportar os parâmetros de rota opcionais corretamente.
Vincent Sels
2
Para aqueles que estão se perguntando por que RouteParamsnão importar para o componente, verifique isso: stackoverflow.com/a/36792261/806202 . A solução é usar ActivatedRoute:route.snapshot.params['routeParam']
Arsen Khachaturyan
89
{path: 'users', redirectTo: 'users/', pathMatch: 'full'},
{path: 'users/:userId', component: UserComponent}

Dessa forma, o componente não é renderizado novamente quando o parâmetro é adicionado.

Martin Cremer
fonte
6
Esta resposta é a melhor. Não renderiza novamente o mesmo componente e não requer vários componentes.
Rex
4
A melhor resposta, mas eu adicionei pathMatch: 'full'para redirecionamento, caso contrário, caminhos, como users/admintambém é redirecionada no meu caso
Valeriy Katkov
4
Esta resposta é apenas a melhor se você estiver de acordo com as barras nos seus URLs, conforme visualizadas no navegador. Considere talvez um valor que represente 'um ID indefinido', por exemplo , /users/allou /users/homeleia 'all' ou 'home' como o ide simplesmente ignore-o se corresponder ao seu valor mágico. Então a primeira linha acima se torna redirectTo: 'users/home'ou o que você decidir. Para mim, uma barra à direita realmente se destaca como algo errado.
Simon_Weaver 16/02/19
@Simon_Weaver Concordo. Eu encontrei outra solução usando uma correspondência que não tem este problema: stackoverflow.com/a/56391974/664533
Wayne Maurer
1
é um feitiço simples, mas bastante inquebrável: D A melhor solução!
Verri
45

É recomendável usar um parâmetro de consulta quando as informações são opcionais.

Parâmetros de rota ou parâmetros de consulta?

Não existe uma regra rígida. Em geral,

prefere um parâmetro de rota quando

  • o valor é obrigatório.
  • o valor é necessário para distinguir um caminho de rota de outro.

prefere um parâmetro de consulta quando

  • o valor é opcional.
  • o valor é complexo e / ou multivariável.

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

Você só precisa retirar o parâmetro do caminho da rota.

@RouteConfig([
{
    path: '/user/',
    component: User,
    as: 'User'
}])
Jp_
fonte
6
Alterar parâmetros de rota opcionais renderiza novamente os componentes, mas alterar queryParams não. Além disso, se você resolver alguns dados antes da navegação da rota, eles serão solicitados sempre que você alterar parâmetros de rota opcionais.
Rakhat
1
Para sua informação, esse link âncora não funciona mais. O novo link parece ser Parâmetros da rota: obrigatório ou opcional?
Spottedmahn
20

Angular 4 - Solução para atender à solicitação do parâmetro opcional:

FAÇA ISSO:

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

Observe que as rotas productse products/:idsão nomeadas exatamente da mesma forma. O Angular 4 seguirá corretamente productspara rotas sem parâmetro e, se um parâmetro seguirproducts/:id .

No entanto, o caminho para a rota sem parâmetro nãoproducts deve ter uma barra final; caso contrário, o angular o tratará incorretamente como um caminho de parâmetro. Então, no meu caso, eu tinha a barra final de produtos e não estava funcionando.

NÃO FAÇA ISTO:

...
{path: 'products/', component: ProductsComponent},
{path: 'products/:id', component: ProductsComponent},
...
ObjectiveTC
fonte
Se os dois vão para o ProductsComponent, como você lida com o parâmetro opcional lá?
Arwin
1
Você pode acessar os parâmetros: id1,: id2, etc, bem como a URL solicitada no ProductsComponent, assim: this.route.url.first () .mergeMap ((url) => {// console.log ('1: alteração de url detectada '+ url); retorne this.route.params.do ((params) => {// console.log (' 2: url + alteração de params detectada '+ params ["id1"] +' '+ params ["id2"]); this.id1 = params ["id1"]; this.id2 = params ["id2"];})})
ObjectiveTC
2
Lembre-se de que você também pode passar datapara o componente, que pode ser diferente para cada rota, mesmo para o mesmo componente. Exemplo {path: 'products', component: ProductsComponent, data: { showAllProducts: true} },poderia ser usado e, em seguida, você verificar showAllProducts. Um pouco melhor, em seguida, procurar um nulo, mas para casos mais simples, provavelmente é bom.
Simon_Weaver 31/01
1
Infelizmente, esta solução impedirá que a Angular reutilize o componente entre produtos e produtos /: id. O componente será reinstanciado.
Kodiak
@Kodiak - Não acredito que isso esteja correto. Meu entendimento é que em app.module.ts, o ProductsComponent é instanciado uma vez e que o mecanismo angular reutiliza esse ProductsComponent instanciado em cada evento navegável (produtos e produtos /: id etc). Você pode explicar ou demonstrar como o ProductsComponent pode ser re-instanciado no código acima e como você evitaria a re-instanciação?
ObjectiveTC
11

A resposta de rerezz é muito boa, mas tem uma falha séria. Faz com que o Usercomponente execute novamente ongOnInit método.

Pode ser problemático quando você faz coisas pesadas lá e não deseja que seja reexecutado quando você alterna da rota não paramétrica para a paramétrica. Embora essas duas rotas tenham como objetivo imitar um parâmetro de URL opcional, não se tornem duas rotas separadas.

Aqui está o que eu sugiro para resolver o problema:

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

Em seguida, você pode mover a lógica responsável por manipular o parâmetro para o UserWithParamcomponente e deixar a lógica base no Usercomponente. O que você fizer User::ngOnInitnão será executado novamente quando você navegar de / user para / user / 123 .

Não esqueça de colocar o <router-outlet></router-outlet>no Usermodelo.

matewka
fonte
Evitar que o componente seja recriado é uma coisa boa se o desempenho for crítico. Eu tenho outra solução que também evita que o componente seja recriado: stackoverflow.com/a/56391974/664533
Wayne Maurer
4

As respostas sugeridas aqui, incluindo a resposta aceita do rerezz que sugere a adição de várias entradas de rota, funcionam bem.

No entanto, o componente será recriado ao mudar entre as entradas da rota, ou seja, entre a entrada da rota com o parâmetro e a entrada sem o parâmetro.

Se você quiser evitar isso, pode criar seu próprio correspondente de rotas que corresponderá às duas rotas:

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

Em seguida, use o matcher na sua configuração de rota:

const routes: Routes = [
    {
        matcher: userPageMatcher,
        component: User,
    }
];
Wayne Maurer
fonte
@KevinBeal Eu implementei alguns matchers que funcionam com o AOT. Qual é o erro que você está recebendo aqui?
Wayne Maurer
Opa Era outra coisa. Meu par trabalha com AOT.
22419 Kevin Beal
isso é um pouco complicado, mas a melhor solução para esse problema
fedor.belov 18/01
4

Com angular4, só precisamos organizar rotas juntas na hierarquia

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

Isso funciona para mim. Dessa forma, o roteador sabe qual é a próxima rota com base nos parâmetros de identificação da opção.

Ravi Jadhav
fonte
1

Deparou-se com outra instância desse problema e, ao procurar uma solução para ele, veio aqui. Meu problema era que eu fazia as crianças e carregava preguiçosamente os componentes para otimizar um pouco as coisas. Em resumo, se você estiver com preguiça de carregar o módulo pai. O principal foi o uso de '/: id' na rota e as queixas de '/' fazer parte dela. Não é o problema exato aqui, mas se aplica.

Roteamento de aplicativo do pai

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

Rotas filho carregadas preguiçosamente

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

Enfrentando um problema semelhante com carregamento lento, eu fiz isso:

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

E então no componente:

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

Deste jeito:

  • A rota sem /é redirecionada para a rota com. Por causa disso pathMatch: 'full', apenas essa rota completa específica é redirecionada.

  • Então, users/:idé recebido. Se a rota real era users/, idé "", faça o check-in ngOnInite aja de acordo; caso contrário, idé o id e prossiga.

  • O restante do componente atua selectedUseré indefinido (* ngIf e coisas assim).

Javier Sedano
fonte