简体   繁体   中英

Get name of key in select

I am trying to populate a select element from JSON, but the JSON is formatted in such a way that the keys hold valuable information. I do not have control over the data format.

I am trying to iterate over the JSON and and grab the key names for the top-level objects in the array. The problem is that because it is an object, I can't seem to grab just its name -- Object.keys() does not work within ng-options due to scoping.

I've tried many variations on the following, with no luck.

<select ng-model="$ctrl.vals"
        ng-options="val as key for (key, val) in $ctrl.data"
        ng-change="$ctrl.onChange()" name="{{$ctrl.name}}"
        size="{{$ctrl.data.length}}" multiple>
</select>

The above returns "0", because it's formatted as 0: [object object]. The closest I have been able to get is [Object Object] returned, but I want the key of that object, and am not sure how to get it.

I have data formatted like this (sample data, not real):

{
"Games": [{
    "Name": "Warhammer 40k 8th Edition",
    "Factions": [{
        "Space Marines": {
            "Units": [{
                "Name": "Primaris Space Marine Captain",
                "Number": 1,
                "Cost": -1,
                "Ability": "When captain enters play you win",
                "AddOns": [{
                    "Name": "My AddOn",
                    "Cost": 0,
                    "Text": "Add an extra Captain",
                    "IsSelected": false
                }],
                "Gear": [{
                    "Name": "Frag Grenade",
                    "Cost": 0
                }]

            }]
        }
        }]
    }]
}

In the context of the JSON above, I want to pass in Factions and see the text "Space Marines" as an option. What am I missing?

If $ctrl.data in your snippets refers to the "Factions" property value as you've written, then the ng-options expression you've used isn't compatible (ie the ... for (key, value) in ... form would require $ctrl.data to be an object, which it isn't).

Subsequently, you should use the array expression form for ng-options , and then you can supply additional functions to rip out the label and model that will be bound when a user selects a particular option.

Here's how you might go about it:

 angular .module('app', []) .controller('ctrl', function () { const $ctrl = this; $ctrl.modelFor = function (obj) { const [key] = Object.keys(obj); return key ? obj[key] : null; }; $ctrl.labelFor = function (obj) { const [key] = Object.keys(obj); return key; }; $ctrl.data = [{ "Space Marines": { "Units": [{ "Name": "Primaris Space Marine Captain", "Number": 1, "Cost": -1, "Ability": "When captain enters play you win", "AddOns": [{ "Name": "My AddOn", "Cost": 0, "Text": "Add an extra Captain", "IsSelected": false }], "Gear": [{ "Name": "Frag Grenade", "Cost": 0 }] }] } }]; }); 
 <div ng-app="app" ng-controller="ctrl as $ctrl"> <select ng-model="$ctrl.vals" ng-options="$ctrl.modelFor(obj) as $ctrl.labelFor(obj) for obj in $ctrl.data"></select> <pre>{{ $ctrl.vals }}</pre> </div> <script src="https://unpkg.com/angular@1.7.8/angular.min.js"></script> 

As an aside, your data looks a bit odd. If each item of $ctrl.data can have multiple keys, then this approach will arbitrarily select the first one (ie ordering isn't guaranteed when iterating over keys).

Transform the data:

 $ctrl.mapped = $ctrl.data.map(_ => {
     var entry = Object.entries(_)[0];
     return { key: entry[0], value: entry[1] };
 });

Then use it in the HTML:

<select ng-model="$ctrl.vals"
        ng-options="item.value as item.key for item in $ctrl.mapped"
        ng-change="$ctrl.onChange()" name="{{$ctrl.name}}"
        size="{{$ctrl.data.length}}" multiple>
</select>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM