Use dataKey to compare item in a Dropdown

UI Components for Angular
Post Reply
czetsuya
Posts: 20
Joined: 19 Apr 2013, 15:13
Location: Philippines
Contact:

07 Apr 2018, 01:57

How can we use the dataKey when setting the value of a dropdown? I have:

<p-dropdown [options]="animeDS" formControlName="anime" placeholder="Select a anime*" optionLabel="code" [autoWidth]="false" [dataKey]="id" [filter]="true"></p-dropdown>

Which is a filter, generated from one datasource which only returns id and code. And then another which is a part of an entity let's say animeShow, which has more anime fields, for example: id, code, description, date_shown, etc. When I try to set the dropdown from this value it fail, because it compares via string comparison. I would like to use the dataKey so whatever fields are added / removed the comparison will not be affected.

elitovsky
Posts: 3
Joined: 10 May 2017, 02:36

11 Jul 2018, 06:14

I don't think this feature works for p-dropDown.
I have been trying to figure it out, but the documentation is sparse, and I was unable to find any working examples online.

Strange, as it's not that uncommon to bind a complex object array to the [options] attribute and have the selection bind to a specific member of the object such as an ID field, especially since there is an optionLabel attribute which seems to work fine.

Would love to see a more concrete implementation such as an optionValue attribute. Not being able to bind the value to a member of the selected option is kind of limiting.

If anyone can post a working snippet, I would definitely appreciate it.

lacoL
Posts: 3
Joined: 07 Sep 2018, 18:40

13 Sep 2018, 18:08

This drives me mad.

So, value is string value of 1.
Options:

Code: Select all

[{ "id": 1, "description": "item1" }, { "id": 2, "description": "item2" }]
Set the dataKey to "id", ngModel is a value of "1"...

Then this comes along and makes me want to burn a book.
objectutils.ts

Code: Select all

public resolveFieldData(data: any, field: any): any {
        if(data && field) {
            if (this.isFunction(field)) {
                return field(data);
            }
            else if(field.indexOf('.') == -1) {
                return data[field];
            }
            else {
                let fields: string[] = field.split('.');
                let value = data;
                for(let i = 0, len = fields.length; i < len; ++i) {
                    if (value == null) {
                        return null;
                    }
                    value = value[fields[i]];
                }
                return value;
            }
        }
        else {
            return null;
        }
}
Issue is that now in the first pass, data is "1" with a field of "id" BUT when it passes the next check to see if the selectedItem should be set when calling;
dropdown.ts

Code: Select all

findOptionIndex(val: any, opts: any[]): number {
        let index: number = -1;
        if(opts) {
            for(let i = 0; i < opts.length; i++) {
                if((val == null && opts[i].value == null) || this.objectUtils.equals(val, opts[i].value, this.dataKey)) {
                    index = i;
                    break;
                }
            }
        }
        
        return index;
}
Issue.... val = "1", opts[0].value = { "id": 1, "description": "item1" }, dataKey = "id"

can you see who this WILL NOT equal and fail every damn time?! So, I set val to be { "id": 1 } cause that should mean data[field] should return 1... BUT NO I need the full object of { "id": 1, "description": "item1" } (MAKES COMPLETE SENSE DO NOT GET ME WRONG, BUT, BUT, I will not store that object JUST THE VALUE OF ID!) but by GOD 4 hours of my life wasted.

This is way over complicated to understand yourself when you want to use dataKey when you cannot re-write your entire backend to use label and value items in lists.

So, because there is NOTHING to help with understanding the dataKey, here it is.

If using dataKey set this to be the key value of which you are storing, i.e. if you have { "id": 1 } then set the dataKey to be "id".

Now, the ngModel connection or even formControl must match the item in the list, so if you have options like above in the first code block the selected item must match this, so if you only get the id from the database you will have to filter your list to a selected item and THEN make sure you flip this back when talking to the server.

Code: Select all

	items: List[] = [];
	selectedItem: List = new List();
	selectedValue = '2';
	
	someSetListFunc() {
		someHttpGetProcess().sub(response => {
			this.items = response.data;
			this.selectedItem = this.items.filter(x => x.id === this.selectedValue)[0];
		});
	}
	
	someSendToServer() {
		myServerObject: Server = new Server();
		myServerObject.someId = this.selectedItem.id;
	}
	
	// <p-dropdown [options]="items" [(ngModel)]="selectedItem" dataKey="id" optionalLabel="description"></p-dropdown>
So, when you adding this to the docs?! Can you understand why I'm unhappy about this?! Had to debug to understand the code, because! there is nothing to say how this works.
Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live - John Woods

mpscheidt
Posts: 2
Joined: 03 Dec 2018, 10:23

03 Dec 2018, 10:27

lacoL wrote:
13 Sep 2018, 18:08
This drives me mad.

...
Quite annoying indeed.

Have you posted a bug report (https://github.com/primefaces/primeng/issues)?

Paddington_the_Bear
Posts: 1
Joined: 15 Jul 2020, 13:41

11 Nov 2020, 10:24

Agreed with the frustration that OP described. I've wasted hours dealing with this. A custom comparison function would solve a lot of the issues.

As mentioned, the issue with DataKey stems from a bug in

Code: Select all

resolveFieldData
, which will always return undefined when using DataKey. DataKey forces the "obj2" to be a primitive value, despite the dropdown values being objects. So even if your select item is an object, then the resolveFieldData comparison will fail!

The simple fix to this is to add a check to the beginning of resolveFieldData for whether or not the data is actually an object; if it isn't just return data:

Code: Select all

static resolveFieldData(data, field) {
        if (typeof(data) !== 'object') return data;
        if (data && field) {
            if (this.isFunction(field)) {
                return field(data);
            }
            else if (field.indexOf('.') == -1) {
                return data[field];
            }
            else {
                let fields = field.split('.');
                let value = data;
                for (let i = 0, len = fields.length; i < len; ++i) {
                    if (value == null) {
                        return null;
                    }
                    value = value[fields[i]];
                }
                return value;
            }
        }
        else {
            return null;
        }
    }
This makes sense because resolveFieldData is attempting to get a property value in some objects.

Post Reply

Return to “PrimeNG”

  • Information
  • Who is online

    Users browsing this forum: Baidu [Spider] and 5 guests