0
votes

Good evening,

I've been having issues with the dropdown menu, particularly with organizing by groups. First of all, it took some time to understand that the array used in options to populate the dropdown needs to be in a very particular way. Fine, my API now returns what it wants and I am able to display the group headings, the individual items, and I can map the selected item to a variable. Very good, but I am only able to map the label (or the value, I'm not sure) of the selected item, and what I really want is other data I have sent along.

Below is the JSON my Angular component receives (you will see I'm a little confused and have wasted properties... I wasn't sure what PrimeNG wanted, and am trying to adapt):

[
    {
        "group": null,
        "label": "First group",
        "value": "First group",
        "items": [
            {
                "id": 1,
                "name": null,
                "label": "Par unité",
                "value": "Par unité",
                "description": null
            },
            {
                "id": 2,
                "name": null,
                "label": "Par employé",
                "value": "Par employé",
                "description": null
            }
        ]
    },
    {
        "group": null,
        "label": "Second group",
        "value": "Second group",
        "items": [
            {
                "id": 7,
                "name": null,
                "label": "Temps entre démande et acceptation",
                "value": "Temps entre démande et acceptation",
                "description": null
            },
            {
                "id": 9,
                "name": null,
                "label": "something else",
                "value": "something else",
                "description": null
            }
        ]
    }
]

HTML:

<p-dropdown [options]="clientReports" 
([ngModel])="selectedReport" (onChange)="onReportChange($event)" placeholder="Choose a report" [group]="true">

TS:

clientReports: any[];
selectedReport: any;

onReportChange(event) {
    this.selectedReport = event.value;
    console.log(this.selectedReport);
}

Issue and desired outcome:

I think because of the onChange call, my variable selectedReport now has the value (or the label...) of the, well, selected report (coming from "items" in the JSON). Great, however I will need to reference the id later on and I can't seem to figure out how.

To be clear, I do not want the id to be displayed on the dropdown menu, but I need that information passed along to selectedReport (or, if I need a second variable I don't care, I just need that data).

In the documentation for the dropdown, I see that there are multiple properties such as optionLabel, optionValue, dataKey, etc. Many of these change the displayed value in the dropdown, others I can't seem to see what they do.

Hopefully someone can help me out with this.

Thanks in advance.

2

2 Answers

1
votes

You should type your objects.. that is what TypeScript is all about. Start with understanding the interfaces required for this control, The dropdown in its grouped mode is expecting to get an array of SelectItemGroup objects. From the primeng source you can see the interfaces structure:

export interface SelectItemGroup {
    label: string;
    value?: any;
    items: SelectItem[];
}
export interface SelectItem<T = any> {
    label?: string;
    value: T;
    styleClass?: string;
    icon?: string;
    title?: string;
    disabled?: boolean;
}

And let say that your report interface is:

export interface ClientReport {
 id:number;
 name:string;
 description:string;
} 

When you are getting your data, map it to get something like:

[
 {
  label: 'group A',
  value: 1,
  items: [
   { 
    label: 'report 1',
    value: { name: 'report no.1', description: 'first report' } as ClientReport
   } as SelectItem,
   { 
    label: 'report 2',
    value: { name: 'report no.2', description: 'second report' } as ClientReport
   } as SelectItem
  ] as SelectItem[]
 } as SelectItemGroup,
 {
  label: 'group B',
  value: 2,
  items: [
   { 
    label: 'report 3',
    value: { name: 'report no.3', description: 'third report' } as ClientReport
   } as SelectItem,
   { 
    label: 'report 4',
    value: { name: 'report no.4', description: 'fourth report' } as ClientReport
   } as SelectItem
  ] as SelectItem[]
 } as SelectItemGroup
] as SelectItemGroup[]

The binded model would be of interface SelectItem so when you log its value, you should see your ClientReport object.

onReportChange(event) {
    console.log(event.value);
}

Notice that you should not set the selected report, it should be binded when you define it in the ([ngModel])

If you want to display customized information in the dropdown option, you can do it using a template. You can set the pTemplate attribute to 'group', 'item' or/and 'selectedItem'. For example:

<ng-template let-group pTemplate="group">
    <div>
        <span>my customized group option:</span> 
        <span>{{group.label}}</span>
    </div>
</ng-template>

<ng-template let-myItem pTemplate="item">
    <div>
        <span>my customized report item option:</span> 
        <div>{{myItem.value.name}}</div>
    </div>
</ng-template>

<ng-template pTemplate="selectedItem">
    <div style="color:blue;">
        <div>{{selectedReport?.value?.name}}</div>
    </div>
</ng-template>

I didn't tested the code, so I'm sorry if there are typos or any other mistakes.

1
votes

See below. I think this will get you what you need, as long as I understand your need.

Assuming your data looks like this:

sourceData = [
    {
        group: null,
        label: 'First group',
        value: 'First group',
        items: [
            {
                id: 1,
                name: null,
                label: 'Par unité',
                value: 'Par unité',
                description: null
            },
            {
                id: 2,
                name: null,
                label: 'Par employé',
                value: 'Par employé',
                description: null
            }
        ]
    },
    {
        group: null,
        label: 'Second group',
        value: 'Second group',
        items: [
            {
                id: 7,
                name: null,
                label: 'Temps entre démande et acceptation',
                value: 'Temps entre démande et acceptation',
                description: null
            },
            {
                id: 9,
                name: null,
                label: 'something else',
                value: 'something else',
                description: null
            }
        ]
    }
];

Your component code could look like this:

clientReports: SelectItemGroup[] = [];
selectedReport: any;

ngOnInit(): void {
    this.clientReports = this.sourceData.map(g => {
        return { label: g.label, items: g.items.map(i => ({ label: i.label, value: i })) };
    });
}
onReportChange(event: any) {
    this.selectedReport = event.value;
    console.log(this.selectedReport);
}

The real interesting part is the map of the source data into the clientReports variable in the ngOnInit hook. The dropdown in the temaple is the same as what you have in your post.

<p-dropdown [options]="clientReports"
    ([ngModel])="selectedReport" (onChange)="onReportChange($event)"
    placeholder="Choose a report" [group]="true">

Give this a try and see how it works for you.