Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
April 16, 2024 06:56 pm GMT

Obtener parmetros de la URL como @Input (Angular 16)

A partir de Angular 16 podemos obtener desde un componente la informacin de una ruta simplemente utilizando @Input. En este artculo veremos el funcionamiento de este nuevo procedimiento, as como algunas consideraciones que tendremos que tener en cuenta a la hora de utilizarlo para evitar problemas potenciales.

Si lo prefieres, el contenido de este artculo tambin lo tienes en formato video aqu.

Tipos principales de informacin en las Rutas

A la hora de desarrollar una aplicacin, es muy comn tener que lidiar con rutas que definen cierta informacin de forma dinmica.

En Angular podemos destacar tres tipos principales de este tipo de informacin:

  • Los parmetros de ruta.
  • Los parmetros de consulta.
  • Y una serie de datos adicionales que puede tener asociada una ruta, tanto de forma manual en su propiedad data como a travs de resolvers.
const routes = [  {     path: 'products/:productId',  // :productId  es un parmetro de ruta    component: ProductDetailsComponent,    data: {      experimental: true    },    resolve: {      product: productResolver    }  },  ...]// Los parmetros de bsqueda se encuentran despus del '?' en la URL// en este caso seran q y maxPrice'myshop.com/catalog?q=teclado&maxPrice=100'  

Procedimiento Tradicional

El procedimiento ms habitual para obtener esta informacin desde un componente consiste en:

  • Inyectar la ruta actual (ActivatedRoute / ActivatedRouteSnapshot)
  • Y extraer la informacin a travs de sus propiedades params, queryParams y data
// Informacin en forma de Observablesexport class ProductDetailsComponent {  route = inject(ActivatedRoute);  productId$ = this.route.params.pipe(map(params => params['productId']));}

Nuevo Procedimiento

Bien pues a partir de Angular 16, esta misma informacin tambin la podremos obtener simplemente aadiendo un @Input en el componente cuyo nombre coincida con el del parmetro o data que queramos obtener.

export class ProductDetailsComponent {  @Input() productId;  }

En el caso de que tuviramos un ruta del tipo catalog?q=teclado con un parmetro de consulta q y quisiramos vincularlo con una propiedad ms descriptiva, podramos conseguirlo indicando el nombre real del parmetro en el alias del @Input.

@Input('q') query;

Cmo activarlo?

Esta nueva caracterstica no viene activada por defecto, por lo que tendremos que activarla manualmente en la configuracin del Router.

Si estamos usando la configuracin modular esto lo podremos conseguir asignando a true la opcin bindToComponentInputs en el mtodo forRoot.

@NgModule({  imports: [    RouterModule.forRoot(      routes,       { bindToComponentInputs: true } // <---    )  ],  exports: [RouterModule],})export class AppRoutingModule {}

Y en el caso de estar usando la configuracin standalone, la podremos activar usando la funcin withComponentInputBinding() a la hora de proveer el Router.

export const appConfig: ApplicationConfig = {  providers: [    provideRouter(routes, withComponentInputBinding()),  ],};

Una vez activada la caracterstica el Router vincular automticamente todos los parmetros y data de las rutas con los inputs del componente cuyo nombre coincida con dichos parmetros.

Orden de prioridad

En el caso de tener mltiples parmetros o data con un mismo nombre.

// sameName para todos ellos {   path: 'somepath/:sameName',  data: {    sameName: true  },  resolve: {    sameName: someResolver  }}'somepath?sameName=aValue'

El orden de prioridad que sigue el Router a la hora de vincular el @Input es el siguiente:

  1. Resolvers
  2. Data
  3. Parmetro de Ruta
  4. Parmetro de Consulta

Consideraciones a tener en cuenta

Los valores de los @Input no estn asegurados hasta ngOnInit

Partiendo del siguiente ejemplo con la configuracin tradicional:

export class ProductDetailsComponent {  id$ = this.route.params.pipe(    map((params) => params['id'])  );  product$ = this.id$.pipe(    switchMap((id) => this.catalogService.getProduct(id))  );  constructor(    private catalogService: CatalogService,    private route: ActivatedRoute  ) {}}

Podramos estar tentados de refactorizarlo con el nuevo procedimiento de la siguiente manera:

export class ProductDetailsComponent {  @Input() id; // <- extraemos el id usando el input  product$ = this.catalogService.getProduct(this.id); //<- utilizamos directamente el id  constructor(private catalogService: CatalogService) {}}

Pero los valores de los @Input en Angular no estn garantizados hasta que el componente haya sido inicializado, o lo que es lo mismo hasta que se ejecute el lifecycle hook de ngOnInit.

Por tanto en este caso tendramos que mover la inicializacin de la propiedad product$ a dicho hook.

export class ProductDetailsComponent implements OnInit{  @Input() id;  product$: Observable<Product | null>;  constructor(private catalogService: CatalogService) {}  ngOnInit() {    this.product$ = this.catalogService.getProduct(this.id);  }}

Podemos perder la reactividad en rutas reutilizables

La opcin anterior de usar el ngOnInit para la inicializacin de las propiedades dependientes de @Input funcionara sin problemas en la mayora de los casos.

Pero en los casos en los que el Router reutilice el componente para la siguiente navegacin (ej.- si navegamos directamente de los detalles de un producto a los detalles de otro), como el ngOnInit no se ejecutara para esa segunda navegacin, la propiedad del producto no se reasignara.

En casos simples como este podramos solucionarlo simplemente sustituyendo la propiedad idpor un setter y mover ah la asignacin del producto.

export class ProductDetailsComponent {  @Input() set id(newId) {    this.product$ = this.catalogService.getProduct(newId);  }  product$: Observable<Product | null>;  constructor(private catalogService: CatalogService) {}}

Para casos complejos mejor usar inputs como Seales (v17.1+)

Para casos ms complejos en los que tengamos mltiples pasos dependientes de los inputs, podramos tambin usar los setters, pero el riesgo de convertir la lgica en ilegible es alto, sobre todo si la complejidad de la lgica es alta.

// routes.ts{  path: 'catalog',  component: CatalogComponent,  resolve: {    products: allProductsResolver,  },},// URL example: myshop.com/catalog?f=teclado&orderBy=priceASCexport class CatalogComponent {  @Input() products!: Products; // <- extraido del resolver  //f y orderBy extraidos de los parmetros de consulta  @Input('f') set nameFilter(val: string) {    this.filteredProducts = filterByProductName([this.products, val ?? null]);    this.orderBy = this._order;  }  _order: string = '';  @Input() set orderBy(val: string) {    this._order = val;    this.orderedProducts = sortByPrice([this.filteredProducts, val ?? null]);  }  filteredProducts: Products = [];  orderedProducts: Products = [];  constructor(private route: ActivatedRoute) {}}

Para estos casos lo ms recomendable es usar una versin declarativa. Teniendo como opciones: los observables y las seales.

En el caso de los observables, la mejor opcin bajo mi punto de vista, es olvidarnos de los Inputs y utilizar el procedimiento anterior, extrayendo la informacin desde ActivatedRoute . Podramos usar un versin mixta utilizando BehaviorSubjects y asignndolos desde los setters de los Inputs.

export class CatalogComponent {  @Input() products!: Products;  _nameFilter$ = new BehaviorSubject('');  @Input('f') set nameFilter(val: string) {    this._nameFilter$.next(val);  }  _order$ = new BehaviorSubject('');  @Input() set orderBy(val: string) {    this._order$.next(val);  }  filteredProducts$?: Observable<Products>;  orderedProducts$?: Observable<Products>;  constructor(private route: ActivatedRoute) {}  ngOnInit() {    this.filteredProducts$ = combineLatest([      of(this.products),      this._nameFilter$,    ]).pipe(map(filterByProductName));    this.orderedProducts$ = combineLatest([      this.filteredProducts$,      this._order$,    ]).pipe(map(sortByPrice));  }}

Pero lo nico que conseguiramos es complicar innecesariamente el cdigo sin ningn beneficio. Por lo que bajo mi punto de vista no merece la pena.

La opcin que si merece la pena y tenemos disponible desde la v17.1+ es la de usar los inputs como seales. El funcionamiento de estos es igual que el de los @Input tradicionales, pero exponen los valores a travs de una seal.

// usando el decorador@Input() id: string;// usando la funcin input (con i minscula)id = input<string>(); // id sera del tipo InputSignal<string>

Usando estos inputs en conjunto con las seales computadas el ejemplo quedara de la siguiente manera:

export class CatalogComponent {  products = input<Products>();  nameFilter = input<string>('', { alias: 'f' }); //<-- tambin podemos usar alias  orderBy = input<string>('');  filteredProducts = computed(() =>    filterByProductName([this.products(), this.nameFilter()])  );  orderedProducts = computed(() =>    sortByPrice([this.filteredProducts(), this.orderBy()])  );}

Siendo esta, bajo mi punto de vista, una de las opciones ms simples y elegantes que hay disponibles ahora mismo.

A simple vista no sabemos de donde procede el valor del Input

Uno de los problemas a la hora de usar este nuevo procedimiento, es que a primera vista no sabemos si los valores de los inputs definidos en un componente vienen de un supuesto padre o si son extrados de alguna parte de la ruta.

Tcnicamente la procedencia del valor es irrelevante para el correcto funcionamiento del componente. Pero si queremos ser ms claros a la hora de definir nuestros inputs, tenemos la opcin de usar alias a la hora de importar las dependencias @Input e input desde @angular/core .

import {..., Input as RouteParam } from '@angular/core'export class ProductDetailsComponent {  @RouteParam() set id(newId) {    this.product$ = this.catalogService.getProduct(newId);  }  ...}
import {   ...,  input as resolvedData,  input as queryParam,} from '@angular/core';export class CatalogComponent {  products = resolvedData<Products>();  nameFilter = queryParam<string>('', { alias: 'f' }); //<-- tambin podemos usar alias  orderBy = queryParam<string>('');  ...}

Conclusin

Este nuevo procedimiento nos puede ser muy til a la hora de simplificar la extraccin de la informacin de las rutas, pero deberemos tener cuidado a la hora de elegir la implementacin para evitar posibles errores potenciales.

Si deseas apoyar la creacin de ms contenido de este tipo, puedes hacerlo a travs nuestroPaypal

YouTubeXGitHub


Original Link: https://dev.to/akotech/obtener-parametros-de-la-url-como-input-angular-16-1ka5

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To