0

I know that the question has already asked on Stackoverflow but the proposed solutions doesn't work.

I need to loop into a list containing categories and items. I want to display only one time the category so I need to test if the category is same that the previous one. If it's the same I don't display it but if not I display the new category. Therefore I want to declare a variable to store the current category name.

Is it possible? If not how can I do to test previous category and new one?

Thanks

Edit : My method to get datas :

getMenuByTruck = () =>{
    const id: string = this.activeRoute.snapshot.params['id'];
    const apiMenu: string = environment.apiAddress + 'TruckMenu/' + id;

    this.repository.getData(apiMenu)
    .pipe(finalize(() => {
      this.updateTruckName();
    }))
      .subscribe(response => {
        this.menu = response as Menu[];
      },
      (error) =>{
        this.errorHandler.handleError(error);
        this.errorMessage = this.errorHandler.errorMessage;
      })
}

and my loop into html code :

<div class="title" *ngFor="let m of menu">
    <h3>{{m.foodCategory?.description}}</h3>
</div>
2
  • why don't you make the category unique before display it? Commented Sep 23, 2021 at 19:39
  • Because I have several categories and I need to display categories with corresponding items. Commented Sep 23, 2021 at 19:50

3 Answers 3

1

You can just make a an array of unique categories, like this:

const data = [
  { category: 1, item: 'Jan' }, 
  { category: 2, item: 'Sarah' }, 
  { category: 1, item: 'Tom' },
  { category: 2, item: 'Nina'}
];

const cats = [...new Set(data.map(item => item.category))];

console.log(cats)

And then loop over the array of categories, using the value to filter your original data and loop over the filtered result in a nested loop.

const data = [
  { category: 1, item: 'Jan' }, 
  { category: 2, item: 'Sarah' }, 
  { category: 1, item: 'Tom' },
  { category: 2, item: 'Nina'}
];

const cats = [...new Set(data.map(item => item.category))];

cats.forEach(cat => {
  console.log(cat)
  data.filter(x => x.category === cat)
    .forEach(x => console.log(x.item))
})

Example in angular project on Stackblitz.

For your followup questions...

You might want to create a service to get your data to keep the request code out of your component and function:

@Injectable({
  providedIn: 'root'
})
export class MenuService {
  readonly API = environment.apiAddress + 'TruckMenu/'

  constructor(
    private http: HttpClient
  ) { }

  getData(id: string): Observable<Menu[]> {
    return this.http.get<Menu[]>(this.API + id)
  }
}

In your component:

menus: Menu[] = []
cats: FoodCategory[] = []

constructor(
  private menuService: MenuService,
  private route: ActivatedRoute,
  ...
) { }

ngOnInit(): void {
  this.menuService.getData(this.route.snapshot.params['id'])
    .subscribe(data => {
      this.menus = data
      this.cats = this.getCats(data)
    })
}
Sign up to request clarification or add additional context in comments.

9 Comments

If you want me to write this out in an example angular project to help you with the template, make a Stackblitz and I will gladly help you out. Another option could be to just sort the array on categories and then style your tags in the template conditionally. It kind of depends on what you are trying to achieve, so some more context in a small Stackblitz project could help.
thanks for your help. Here is an example link. I want to display like this: Catgory 1: - Jan - Tom Category 2: - Sarah - Nina Therefore I need to store previous category to compare with next category.
@Paintbox Here's a simple example: stackblitz.com/edit/angular-ivy-4fd6b9?file=src/app/… You could also create a custom pipe to filter an array if you think the *ngIf is ugly.
My datas comes from a Web Api. I'm having trouble adapting you example to my case. All the informations are in one array.
I have edited my question by adding the code.
|
1

You can create a new object type variable that will have items from original data with unique categories as key.

Suppose we have original data as

originalData = [
    { category: 1, item: 'Jan' },
    { category: 2, item: 'Sarah' },
    { category: 1, item: 'Tom' },
    { category: 2, item: 'Nina' },
  ];

We will declare a new empty object variable. Using reduce on originalData we will check if there is already an entry in the acc by key as category name. If it is there we will copy all those previous items (values), add a new item to it else we will just pass currentObj as an array.

itemsByCategory = {};

this.itemsByCategory = this.originalData.reduce((acc, currentObj) => {
  acc[currentObj.category] = acc[currentObj.category]
    ? [...acc[currentObj.category], currentObj]
    : [currentObj];
  return acc;
}, {});

getter to iterate itemsByCategory

  get categoryKeys(): string[] {
    return Object.keys(this.itemsByCategory);
  }

In template

<ul *ngFor="let key of categoryKeys">
  <li>
    Category : {{ key }}
    <p *ngFor="let obj of itemsByCategory[key]">
      {{ obj.item }}
    </p>
  </li>
</ul>

Demo

If your menu has category as object then see this

1 Comment

This works too, I just rather use a set to get the unique categories.
0

Thanks to you I finaly founded a solution to my problem. In the ts file a property category and a method:

categories: string[];
...
getCats(data: Menu[]): string[]{
    return [...new Set(data.map((item) => <string>item.foodCategory?.description))];
}

and in the Html file this loop:

<div class="title" *ngFor="let category of categories">
      <h2>Les <span>{{category}}</span></h2>
      <div *ngFor="let m of menu">
        <div *ngIf="m.foodCategory?.description == category">
          <ul>
            <li class="wow fadeInUp" data-wow-duration="300ms" data-wow-delay="300ms">
            <div class="item">
             <div class="item-title">
               <span>{{m.price}} €</span>
               <h3>{{m.title}}</h3>
               <div class="border-bottom"></div>
             </div>
             <p>{{m.ingredients}}</p>
            </div>
           </li>
         </ul>   
       </div>
</div> 

Thanks to both of you for your help.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.