Parámetro de ruta opcional angular 2

180

¿Es posible tener un parámetro de ruta opcional en la ruta Angular 2? Probé la sintaxis Angular 1.x en RouteConfig pero recibí el siguiente error:

"EXCEPCIÓN ORIGINAL: Ruta" / usuario /: id? "Contiene"? "Que no está permitido en una configuración de ruta".

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

Respuestas:

298

Puede definir múltiples rutas con y sin parámetro:

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

y maneje el parámetro opcional en su componente:

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

    if (paramId) {
        ...
    }
}

Consulte también el tema relacionado de github: https://github.com/angular/angular/issues/3525

rerezz
fuente
11
Corríjame si me equivoco, pero esta solución funcionó para mí solo cuando se invirtió el orden de las rutas en la matriz, es decir, la ruta con el parámetro ocurrió antes que la otra. Hasta que hice eso, el enrutador solo coincidía con la ruta sin el parámetro.
Aviad P.
10
¿Se sigue aplicando esta solución? Noté que pasar de "Usuario" a "Usuario nuevo" volverá a crear una instancia del componente 'Usuario'
teleaziz
19
antiguo pero un problema importante con este enfoque es que cada ruta se trata como una ruta única y hace imposible la reutilización de componentes.
Agonía
44
Como señaló @teleaziz, agregar el parámetro volverá a representar el componente. Para evitar esto, la respuesta de Martin Cremer; agregar una raíz 'redirectTo' con un valor de parámetro en blanco, funcionó muy bien para mí: stackoverflow.com/a/49159166/1364650 , pero eso es bastante extravagante, creo que deberían admitir parámetros de ruta opcionales correctamente.
Vincent Sels
2
Para aquellos que se preguntan por qué RouteParamsno importar a componentes, verifique esto: stackoverflow.com/a/36792261/806202 . La solución es usar ActivatedRoute:route.snapshot.params['routeParam']
Arsen Khachaturyan
89
{path: 'users', redirectTo: 'users/', pathMatch: 'full'},
{path: 'users/:userId', component: UserComponent}

De esta forma, el componente no se vuelve a representar cuando se agrega el parámetro.

Martin Cremer
fuente
66
Esta respuesta es la mejor. No vuelve a representar el mismo componente y no requiere múltiples componentes.
Rex
44
La mejor respuesta, pero agregué pathMatch: 'full'para redirigir, de lo contrario, las rutas como users/admintambién se redirigen en mi caso
Valeriy Katkov
44
Esta respuesta es solo la mejor si está de acuerdo con las barras diagonales finales en sus URL como se ve en el navegador. Considere quizás un valor que represente 'una identificación indefinida', por ejemplo , /users/allo /users/homelea 'todo' o 'inicio' como el idy simplemente ignórelo si coincide con su valor mágico. Entonces la primera línea de arriba se convierte redirectTo: 'users/home'o lo que sea que decidas. Para mí, un corte final realmente se destaca como algo incorrecto.
Simon_Weaver
@Simon_Weaver Estoy de acuerdo. Encontré otra solución usando un emparejador que no tiene este problema: stackoverflow.com/a/56391974/664533
Wayne Maurer
1
es un hechizo simple pero bastante irrompible: D ¡La mejor solución!
Verri
45

Se recomienda utilizar un parámetro de consulta cuando la información es opcional.

Parámetros de ruta o parámetros de consulta?

No hay una regla estricta. En general,

prefiera un parámetro de ruta cuando

  • Se requiere el valor.
  • el valor es necesario para distinguir una ruta de ruta de otra.

prefiera un parámetro de consulta cuando

  • El valor es opcional.
  • El valor es complejo y / o multivariado.

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

Solo necesita sacar el parámetro de la ruta de la ruta.

@RouteConfig([
{
    path: '/user/',
    component: User,
    as: 'User'
}])
Jp_
fuente
66
Cambiar los parámetros de ruta opcionales vuelve a procesar los componentes, pero cambiar queryParams no. Además, si resuelve algunos datos antes de la navegación de ruta, se solicitará cada vez que cambie los parámetros de ruta opcionales.
Rakhat
1
FYI, ese enlace de anclaje ya no funciona. El nuevo enlace parece ser Parámetros de ruta: ¿obligatorio u opcional?
spottedmahn
20

Angular 4: solución para abordar el orden del parámetro opcional:

HACER ESTO:

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

Tenga en cuenta que las rutas productsy products/:idse nombran exactamente igual. Angular 4 seguirá correctamente las productsrutas sin parámetro, y si es un parámetro, seguirá products/:id.

Sin embargo, la ruta para la ruta sin parámetros noproducts debe tener una barra inclinada final; de lo contrario, angular la tratará incorrectamente como una ruta de parámetros. Entonces, en mi caso, tuve la barra inclinada final para los productos y no estaba funcionando.

NO HAGAS ESTO:

...
{path: 'products/', component: ProductsComponent},
{path: 'products/:id', component: ProductsComponent},
...
ObjectiveTC
fuente
Si ambos van al Componente de Productos, ¿cómo maneja el parámetro opcional allí?
Arwin
1
Puede acceder a los parámetros: id1,: id2, etc., así como a la url solicitada en ProductsComponent de la siguiente manera: this.route.url.first () .mergeMap ((url) => {// console.log ('1: cambio de url detectado '+ url); devuelve this.route.params.do ((params) => {// console.log (' 2: cambio de url + params detectado '+ params ["id1"] +' '+ params ["id2"]); this.id1 = params ["id1"]; this.id2 = params ["id2"];})})
ObjectiveTC
2
Recuerde que también puede pasar dataal componente, que puede ser diferente para cada ruta incluso al mismo componente. Se {path: 'products', component: ProductsComponent, data: { showAllProducts: true} },podría usar un ejemplo y luego lo verificará showAllProducts. Un poco más agradable que buscar un valor nulo, pero para los casos más simples, probablemente esté bien.
Simon_Weaver
1
Desafortunadamente, esta solución evitará que Angular reutilice el componente entre productos y productos /: id. El componente se volverá a instanciar.
Kodiak
@Kodiak: no creo que eso sea correcto. Tengo entendido que en app.module.ts, el Componente de Productos se instancia una vez, y que el motor angular reutiliza ese Componente de Productos en cada evento navegable (productos y productos /: id, etc.). ¿Puede explicar o demostrar cómo ProductsComponent podría volver a crear instancias en el código anterior y cómo evitaría la reinicialización?
ObjectiveTC
11

La respuesta de Rerezz es bastante agradable, pero tiene una falla grave. Hace que el Usercomponente vuelva a ejecutar el ngOnInitmétodo.

Puede ser problemático cuando haces cosas pesadas allí y no quieres que se vuelva a ejecutar cuando cambias de la ruta no paramétrica a la paramétrica. Aunque esas dos rutas están destinadas a imitar un parámetro de url opcional, no se convierten en 2 rutas separadas.

Esto es lo que sugiero para resolver el problema:

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

Luego puede mover la lógica responsable de manejar el parámetro al UserWithParamcomponente y dejar la lógica base en el Usercomponente. Lo que hagas User::ngOnInitno se volverá a ejecutar cuando navegues de / user a / user / 123 .

No se olvide de poner el <router-outlet></router-outlet>de la Userplantilla 's.

matewka
fuente
Evitar que el componente se vuelva a crear es bueno si el rendimiento es crítico. Tengo otra solución que también evita el componente que se está recreando: stackoverflow.com/a/56391974/664533
Wayne Maurer
4

Las respuestas sugeridas aquí, incluida la respuesta aceptada de rerezz que sugiere agregar múltiples entradas de ruta funcionan bien.

Sin embargo, el componente se volverá a crear al cambiar entre las entradas de ruta, es decir, entre la entrada de ruta con el parámetro y la entrada sin el parámetro.

Si desea evitar esto, puede crear su propio emparejador de ruta que coincida con ambas rutas:

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

Luego use el matcher en su configuración de ruta:

const routes: Routes = [
    {
        matcher: userPageMatcher,
        component: User,
    }
];
Wayne Maurer
fuente
@KevinBeal He implementado bastantes coincidencias que funcionan con AOT. ¿Cuál es el error que estás recibiendo aquí?
Wayne Maurer
Ups Era algo más. Mi matcher trabaja con AOT.
Kevin Beal
esto es un poco complicado pero la mejor solución a este problema
fedor.belov
4

Con angular4 solo necesitamos organizar las rutas juntas en jerarquía

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

Esto funciona para mi. De esta forma, el enrutador sabe cuál es la siguiente ruta en función de los parámetros de ID de opción.

Ravi Jadhav
fuente
1

Me encontré con otra instancia de este problema, y ​​en la búsqueda de una solución llegó aquí. Mi problema era que estaba haciendo a los niños y cargando de forma diferida los componentes también para optimizar un poco las cosas. En resumen, si eres perezoso cargando el módulo principal. Lo principal fue que usé '/: id' en la ruta, y son quejas de que '/' sea parte de ella. No es el problema exacto aquí, pero se aplica.

Enrutamiento de aplicaciones del padre

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

Rutas secundarias cargadas perezosas

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

Ante un problema similar con la carga diferida, he hecho esto:

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

Y luego en el 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);
  }

De esta manera:

  • La ruta sin /se redirige a la ruta con. Debido a esto pathMatch: 'full', solo se redirige dicha ruta completa específica.

  • Entonces, users/:idse recibe. Si la ruta real fue users/, ides "", así que regístrese ngOnInity actúe en consecuencia; de lo contrario, ides la identificación y proceder.

  • El resto del componente actúa sobre selectedUsero no indefinido (* ngIf y cosas así).

Javier Sedano
fuente