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);
}
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.
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);
}
}
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.
That was really helpful, thanks. The heavy lifting was never meant actually, my api does it all, so I had to do some tweaks to make your code work, but wouldn't be able to figure it out without your answer.
@user2463644 I saw your update, well done. Happy coding :)!