Warm tip: This article is reproduced from serverfault.com, please click

Problem with angular showing adverts by category

发布于 2020-11-28 13:09:03

I have symfony5/angular10 project, in angular three components: categories (which I use to create a menu - simple list of categories), adverts (list all adverts), single advert (shows advert by id).

Based on these components in app-routing I got three routes:

{ path: 'adverts', component: AdvertsComponent },
{ path: 'advert/category/:name', component: AdvertsComponent },
{ path: 'advert/:id', component: AdvertSingleComponent },

First and last route work fine, I can list all ads and show single ad, but am unable to list ads by category - I get same result as when listing all ads (network inspector shows the same route is being hit). I checked my api with Postman so I know all three routes work fine, so I guess the problem is with my angular code.

I suspect adverts component

import { Component, OnInit } from '@angular/core';
import { DatePipe } from '@angular/common';

import { Advert } from '../advert';
import { AdvertService } from '../advert.service';

@Component({
    selector: 'app-adverts',
    templateUrl: './adverts.component.html',
    styleUrls: ['./adverts.component.css']
})
export class AdvertsComponent implements OnInit {

    adverts: Advert[];

    constructor(private advertService: AdvertService, public datepipe: DatePipe) {}

    ngOnInit() {
        this.getAdverts();
    }

    getAdverts(): void {
        this.advertService.getAdverts()
        .subscribe(adverts => this.adverts = adverts);
    }

    getAdvertsByCategory(name): Advert {
        return this.adverts.find(advert => advert.category === name);
    }

}

As you can see ngOnInit has only getAdverts method, if I add getAdvertsByCategory there, in the console I get this.adverts is undefined.

Any clues?

UPDATE

Thanks to user2403735's answer and his note on doing heavy lifting in the api rather that angular, I came up with this code, fetching prefiltered date from api in order to avoid client side filtering.

See the line commented out in the ngOnInit method? It's because snapshot won't work if the html component is reused, if I clicked one category and then another, the view wouldn't update with the second category data, it would just keep the data from the first clicked category. Hence the need to use different approach - check out this video for an explanation Angular Tutoria 26 - ParamMap Observable

import { ActivatedRoute, ParamMap } from '@angular/router';
...
constructor(private advertService: AdvertService, private route: ActivatedRoute, public datepipe: DatePipe) {}

ngOnInit() {
    //let categoryName = this.route.snapshot.paramMap.get('name');
    this.route.paramMap.subscribe((params: ParamMap) => {
        let categoryName = params.get('name');
        if (categoryName){
            this.getAdvertsByCategory(categoryName);
        } else {
            this.getAdverts();
        }
    });
}

getAdverts(): void {
    this.advertService.getAdverts()
    .subscribe(adverts => this.adverts = adverts);
}

getAdvertsByCategory(name): void {
    this.advertService.getAdvertsByCategory(name)
    .subscribe(adverts => this.adverts = adverts);
}
Questioner
user2463644
Viewed
0
user2403735 2020-11-30 06:01:50

You use the same component (AdvertsComponent) for two routes. However, on initialization of the component, _AdvertsComponent loads all adverts, because of this rule:

ngOnInit() {
    this.getAdverts();
}

The component does not know when to apply the filter. To solve this you can do two things.

  1. The easy way is to create a new component where you do exactly the same, but apply the filter immediately.
export class CategoryComponent implements OnInit {

    adverts: Advert[];
    
    ...


    ngOnInit() {
        const categoryName = this.route.snapshot.paramMap.get("name")
        this.getAdvertsByCategory(categoryName);
    }

    getAdvertsByCategory(categoryName) {
        this.getAdverts();
        this.adverts.find(advert => advert.category === categoryName);
    }

    getAdverts(): void {
        this.advertService.getAdverts()
        .subscribe(adverts => this.adverts = adverts);
    }
}
  1. In the current component, check if a category name is set, and if it is, apply the filter:
export class AdvertsComponent implements OnInit {

    adverts: Advert[];

    ....

    ngOnInit() {
        const categoryName = this.route.snapshot.paramMap.get("name");

        if(categoryName) {
          this.getAdvertsByCategory(categoryName);
        } else {
          this.getAdverts();
        }
        
    }

    getAdvertsByCategory(name) {
        this.getAdverts()
        this.adverts.find(advert => advert.category === name);
    }

    getAdverts(): void {
        this.advertService.getAdverts()
        .subscribe(adverts => this.adverts = adverts);
    }
}

NOTE: In general I would suggest to apply the filters in your API instead of doing the heavy lifting on the client side. So in your http request, apply params to filter out data in your API.