LStarky LStarky - 29 days ago 9
Javascript Question

Load array of Enum values from Json file

I have a local JSON file containing all of my Enum key-value pairs and would like to load them into an array that I can use easily.

enum.json

{
"AbsenceCode": {
"E": "Excused",
"U": "Unexcused"
},
"ActiveInactive": {
"A": "Active",
"I": "Inactive"
},
"AuthenticationLog": {
"1": "Staff",
"2": "ParentAccess",
"3": "StudentAccess"
},
"YesNo": {
"0": "Yes",
"1": "No"
}
}


In my Javascript code, I want to load all of the key-value pairs into an array or object that allows me to easily access them, with the end goals of (a) doing value lookup and (b) creating select boxes.

I started something like this but I'm not wrapping my mind around it correctly and also somewhat unsure of whether this should be done with an array or an object, and whether JavaScript allows the type of array necessary to do this.

// load enumData
var enumKeys = $.getJSON("enum.json", function(json) {
var array = [];
for (var key in json) {
var item = json[key];
for (var keyvalue in item) {
var value = item[keyvalue];
}
array.push(parsed[key])
}
});
// test enumData
console.log(enumKeys["YesNo"]);

// lookup value of key
console.log(enumKeys["AbsenceCode"]["U"]);


In my Aurelia template, I would want something like this:

<template>
<select ref="absencecode">
<option repeat.for="keyvalue of enumKeys.AbsenceCode" value="${keyvalue.key}">${keyvalue.value}</option>
</select>
</template>


My code is "inspired" by the answers to a lot of other similar cases but I didn't find any that matched this exact scenario. Any help would be appreciated! What code should I use to load enumKeys? How do I use the loaded array/object?

Answer

You could use a Value Converter to process object. In fact, there is a nice example for that in the [Documentation, last section].

Applying above example to your case, it's even possible to process objects without any prior transformation.

Gist demo: https://gist.run/?id=4514caa6ee7d40df2f7cfe2605451a0e

I wouldn't say it’s the most optimal solution, though. You might want to transform the data somehow before passing it to repeat.for. Just showing a possibility here.

Template:

<!-- First level properties -->
<div repeat.for="mainKey of data | keys">
    <label>${mainKey}</label>

    <!-- Sublevel - Value Object properties -->
    <select>
        <option value="">---</option>
        <option repeat.for="code of data[mainKey] | keys" 
                value="${code}">${data[mainKey][code]}</option> 
    </select>        
</div>

keys value converter:

export class KeysValueConverter {
  toView(obj) {
    return Reflect.ownKeys(obj);
  }
}

Update:

But how do I target one specific item without having to iterate over all of them?

I've extended the original gist demo, you can check it out.

This would work, but it wouldn't be reusable

<label>Absence Code</label>

<select>
    <option value="">---</option>
    <option repeat.for="code of data.AbsenceCode | keys" 
            value="${code}">${data.AbsenceCode[code]}</option> 
</select>

A better way would be to create a custom element

(Note: <require> is there for demo purposes. Normally, you'd add it to globalResources.)

Organizing above template into a custom element with source and name bindable properties:

  • source: your data object
  • name: first-level property of data object (e.g. AbsenceCode)

enum-list.html

<template>

    <require from="./keys-value-converter"></require>

    <label>${name}</label>
    <select name="${name}" class="form-control">
      <option value="">---</option>
      <option repeat.for="code of source[name] | keys" value="${code}">${source[name][code]}</option> 
    </select>

</template>

You can also use name property in conjunction with aurelia-i18n to display a meaningful label. E.g. ${name | t}.

enum-list.js

import {bindable} from 'aurelia-framework';

export class EnumList {

  @bindable source;
  @bindable name;

}

Usage

Individual dropdowns:

<enum-list source.bind="data" name="AbsenceCode"></enum-list>
<enum-list source.bind="data" name="AuthenticationLog"></enum-list>

Since <enum-list> has all the data, its name property can also be changed at runtime! :)

<label>Type</label>
<select class="form-control" value.bind="selectedType"> 
  <option repeat.for="mainKey of data | keys" value="${mainKey}">${mainKey}</option> 
</select>
<br>
<enum-list source.bind="data" name.bind="selectedType"></enum-list>