I'm trying to alphabetically sort data which will be presented in a select form field. The data will be extracted from an HTTP service, and includes four categories: bread, vegetables, fruits and dairy. This is the pipe I use:
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'orderBy'
})
export class OrderByChildPipe implements PipeTransform {
transform(array: any, field: string): any[] {
array.sort((a: any, b: any) => {
if (a[field] < b[field]) {
return -1;
} else if (a[field] > b[field]) {
return 1;
} else {
return 0;
}
});
return array;
}
}
This is is the ts component:
import { Component, OnInit } from '@angular/core';
import { OrderByChildPipe } from 'src/app/pipes/order-by-child.pipe';
@Component({
selector: 'app-product-form-component',
templateUrl: './product-form-component.component.html',
styleUrls: ['./product-form-component.component.css']
})
export class ProductFormComponentComponent implements OnInit {
categories$:any;
constructor(private http:HttpClient) { }
ngOnInit(): void {
this.http.get('http://localhost:3000/product-categories').subscribe(res=> {
this.categories$ = res;
console.log(this.categories$)
})
}
}
This is the relevant part from the HTML view:
<label for="category">Category</label>
<select id="category" *ngFor="let c of categories$ | orderBy:'name'" type="text" >
<option value=""></option>
<option value="">{{c.bread.name}}</option>
<option value="">{{c.dairy.name}}</option>
<option value="">{{c.vegetables.name}}</option>
<option value="">{{c.fruits.name}}</option>
</select>
</div>
And this is the db.json:
"product-categories": [
{
"bread": {
"name":"Bread"
},
"vegetables": {
"name":"Vegetables"
},
"dairy": {
"name":"Dairy"
},
"fruits": {
"name":"Fruits"
}
}
]
Now I know i'm not doin it correctly since I'm populating all of the options manually and this is why the pipe doesn't work. Also, I feel it is a more tedious approcach. I just need someone to point me to the correct way of doing this. The final result should be all the options sorted alphabetically (using the pipe) once the user selects the category box. Meaning:
Bread Dairy Fruits Vegetables
Thanks!
CodePudding user response:
I think your issue is not the pipe itself, but the data structure (hint: use types to ensure that you are doing the correct thing :)).
Let's assume that the following data structure coming from the HTTP request is the following (copied from your db.json):
[
{
bread: {
name: 'Bread',
},
vegetables: {
name: 'Vegetables',
},
dairy: {
name: 'Dairy',
},
fruits: {
name: 'Fruits',
},
},
]
This is basically an array containing exactly one object with different keys. In order to transform this into an array where sorting makes sense, you should extract the values from it:
this.categories$ = Object.values(res[0]);
After that you can use *ngFor
in order to loop over this array and create an <option>
element for each of these entries (you did this for your select):
<select id="category" type="text" >
<option *ngFor="let c of categories$ | orderBy: 'name'" [value]="c.name">{{c.name}}</option>
</select>
After that your pipe works as expected.
Hints:
- You should always use types. Even if you don't know the exact type of the array, that should be sorted, you know at least that it should be an array.
- The trailing dollar sign is usually a convention used for defining observables. You can just assign your observable to
categories$
and use theasync
pipe. This way you won't have to subscribe manually to it.
CodePudding user response:
ok I changed up a few stuff and it still not working:
Pipe:
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'orderBy'
})
export class OrderByChildPipe implements PipeTransform {
transform(array: any, field: any): any[] {
array.sort((a: any, b: any) => {
if (a[field].localCompare(b[field])) {
return -1;
} else if (a[field].localCompare(b[field])) {
return 1;
} else {
return 0;
}
});
return array;
}
}
t.s:
export class ProductFormComponentComponent implements OnInit {
categories$:Object[];
constructor(private http:HttpClient) { }
ngOnInit(): void {
this.http.get<Object[]>('http://localhost:3000/product-categories').pipe(map((res:Object[])=> {
this.categories$ = Object.values(res[0]);
console.log(this.categories$)
}))
}
view:
<div >
<label for="category">Category</label>
<select id="category" type="text" >
<option value=""></option>
<option *ngFor="let c of categories$ | orderBy:'name'" [value]="c.name">{{c.name}}</option>
</select>
</div>
No error in VS Code now, but on the console it says:
ERROR TypeError: Cannot read properties of undefined (reading 'sort')
What does it mean?