No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Url Parameters / Paginación

8/23
Recursos

Los endpoints del tipo GET suelen recibir información por parámetros de URL. Por ejemplo:

  • https://example.com/api/productos?offset=0&limit=10
  • https://example.com/api/productos?q=XXXXX

Pasaje de parámetros en Angular

Angular posee un método sencillo para construir varias URL con parámetros para realizar consultas en API.

// services/api.service.ts
@Injectable({
  providedIn: 'root'
})
export class ApiService {

  constructor(
    private http: HttpClient,
  ) { }

  getProductsParams(offset: number, limit: number): Observable<any> {
    return this.http.get<Product[]>(`https://example.com/api/productos`, { params: { offset, limit } });
  }
}

Al avanzar al segundo argumento de las peticiones tipo GET un objeto params, Angular automaticamente convertirá las variables que entregues en ?offset=0&limit=10, utilizando sus respectivos nombres.

En este ejemplo, las variables offset y limit suelen utilizarse para crear un paginador de registros. Offset (a veces también llamado “skip”) indica desde qué posición de la cantidad total de registros el backend tiene que devolver y Limit la cantidad total.

// components/catalogo/catalogo.components.ts
getProductsParams(): void {
    this.apiService.getProductsParams(0, 10)
      .subscribe(res => {
        this.productos = res;
      });
}

Al realizar la petición al backend, estamos indicando que devuelva desde la posición 0 un total de 10 registros. Si pasáramos los parámetros (30, 15), estaríamos indicando que desde el registro número 30 nos devuelva una cantidad de 15.

Un buen backend suele permitir este tipo de paginación y dinamismo en sus endpoints que devuelven grandes cantidades de registros.


Contribución creada por: Kevin Fiorentino.

Aportes 8

Preguntas 0

Ordenar por:

Los aportes, preguntas y respuestas son vitales para aprender en comunidad. Regístrate o inicia sesión para participar.

No sé si es una mala práctica… pero para no duplicar la funcionalidad yo simplemente llamo a LoadMore dentro del NgOnInit

ngOnInit(): void {
    this.loadMore()
  }

Si se ejecuta el método getAllProducts(), la condición if(limit && offset) al principio se evalula a falso porque offset inicia con un valor de 0, entonces al evaluar un 0 es falso

En vez de utilizar la función concat se puede utilizar la función push de la siguiente manera:

loadMore(){
    this.productsService.getProductsByPage(this.limit, this.offset).subscribe((data: Product[]) => {
      this.products.push(...data)
      this.offset += this.limit
    })
  }

Para el caso de angular 10, el httpParams me toma solo valores string, así que tuve que transformarlos al momento de enviar la petición; De lo contrario se genera :

No overload matches this call.
Overload 3 of 15, ‘(url: string, options?: { headers?: HttpHeaders | { [header: string]: string | string[]; }; observe?: “body”; params?: HttpParams | { [param: string]: string | string[]; }; reportProgress?: boolean; responseType?: “json”; withCredentials?: boolean; }): Observable<…>’, gave the following error.

Modifique la validación en el servicio:

  getAllProducts(limit?: number, offset?: number) {
    let params = new HttpParams();
    if (limit !== undefined && offset !== undefined) {
      params = params.set('limit', limit);
      params = params.set('offset', offset);
    }
    return this.http.get<Product[]>(this._apiUrl, { params });
  }

Y eliminé la función loadMore() y únicamente llamó a una nueva función loadData()

  loadData() {
    this.productsService.getAllProducts(this.limit, this.offset)
      .subscribe(data => {
        this.products = this.products.concat(data);
        this.offset += this.limit;
      });
  }

Pensando más enfocado al usuario, he añadido un limite de productos por página editable por el usuario, lo comparto a continuación:

HTML:

<button (click)="createNewProduct()">Create</button>
<section class="limitSelector">
  <label for="limitInput">Limite de productos por página</label>
  <input type="number" name="limitInput" [(ngModel)]="limitInput" step="5" min="10" max="50">
  <button *ngIf="limit != limitInput; else limitInputDisabled" (click)="changePageLimit()"> Guardar </button>
  <ng-template #limitInputDisabled>
    <button disabled> Guardar </button>
  </ng-template>
</section>

COMPONENT - atributos:

limit = 10;
  offset = 0;
  limitInput = 10;

COMPONENT - ngOnInit:

ngOnInit(): void {
    // Default configuration of pagination
    this.limit = 10;
    this.offset = 0;
    this.limitInput = 10;
    this.loadProductsBypage();
  }

COMPONENTE - funciones:"

loadProductsBypage() {
    this.productsService.getProductsByPage(this.limit, this.offset)
    .subscribe(data => {
      this.products = this.products.concat(data);
    });

  }

  loadMore() {
    this.offset += this.limit;
    const data = this.loadProductsBypage();
  }

  changePageLimit() {
    this.limit = this.limitInput
    this.offset = 0;
    this.products = [];
    this.loadProductsBypage();
  }

Sería bueno tener material teórico además de las clases con código (las cuales son buenas) pero es más que nada para tener algo más conceptual.

Para el reto de estado inicial de limit y offset tengo dos opciones:

  1. la según es como menciono un compañero en un aporte, que seria llamando el metodo this.loadMore() en el ngOnInit().