I am new to Angular and am trying to make a basic prototype to sell the company on using AngularJS. I've got all the basics down, but in trying to meet the way marketing wants options to show in certain drop-down lists, I am running into trouble. They want a visual like this (without the bullets) where the bold items are optgroup elements:
The HTML looks like this:
<fieldset id="fs_frameColor">
<label class="col-xs-4" for="frameColor">Frame Color *</label>
<select class="col-xs-8" id="frameColor"
ng-model="vm.frameColor"
ng-change="vm.frameColorUpdated()"
ng-options="color.label for color in vm.frameColors">
</select>
</fieldset>
I was hoping I could have the frameColors array full of objects that contained label and value properties, but additionally something like a type and level property so that if the type was option, I could output a regular <option>
element. If the type was optiongroup, I could output a regular <optgroup>
.
I know up to this point it is all simple enough. You could, for example, introduce a group by in the ng-options directive and the directive would automatically create optgroup elements based on the group by. However, because of the way marketing wants these to appear, the nesting is multi-level.
Because of the multi-level nature, that is why I was hoping to be able to use type and level properties on objects in my array so that, for example, the "TwoTone" item above would have type: "OptionGroup" so I could output an <optgroup/>
element and then level:1 so it would be indented as normal. Then its child "WhtExt" item would have type: "OptionGroup", but a level:2 so I could output an <optgroup/>
element with its normal bold text, but that text be indented a couple spaces. That group's child option elements would have something like type:"Option" and level:3 so <option/>
elements would be output with the text indented sufficiently to make them appear as children to "WhtExt" parent optgroup. Not shown here, but if they wanted child option elements to show under "TwoTone" optgroup before that "WhtExt" child optgroup, that object in the data array would have type:"Option" and level:2.
I've had success doing this with directives, but only if I embed the <select><option/><option/><optgroup><optgroup><option/><option/></optgroup><optgroup><option/><option/></optgroup></optgroup></select>
code directly in my html and add the needed directives to the specific child elements. However, long-term this data will come from some API so I am trying to determine a way to accomplish all this custom multi-tier group using ng-options tied to the values of properties in the array data.
Since this is meant to be a prototype, the input data is fluid and I am trying to come up with a structure that will convey enough information for an angular directive (or some other angular mechanism) to output the result shown above. Here is an example of the hard-coded data I am binding to.
vm.frameColors = [
{
value: "BZ",
label: "Bronze",
type: "Option",
level: "0"
},
{
value: "W",
label: "White",
type: "Option",
level: "0"
},
{
value: "B",
label: "Beige",
type: "Option",
level: "0"
},
{
value: "TwoTone",
label: "TwoTone",
type: "OptionGroup",
level: "0"
},
{
value: "WhtExt",
label: "WhtExt",
type: "OptionGroup",
level: "1"
},
{
value: "WB",
label: "W Ext/Brz Int",
type: "Option",
level: "2"
},
{
value: "WDO",
label: "W Ext/Drk Oak Int",
type: "Option",
level: "2"
},
{
value: "BrzExt",
label: "BrzExt",
type: "OptionGroup",
level: "1"
},
{
value: "BW",
label: "Brz Ext/Wht Int",
type: "Option",
level: "2"
},
{
value: "BDO",
label: "Brz Ext/Drk Oak Int",
type: "Option",
level: "2"
},
];
Any help would be greatly appreciated.
-Mike
@DianaR I I finally got what I wanted by splitting logic out to both the view html as well as to an angular directive. Here is what the view html looks like now where I am dealing with gridStyle object array rather than the previous frameColor array, but all the same:
<fieldset id="fs_gridStyle">
<label class="col-xs-4" for="gridStyle">Grid Style *</label>
<select class="col-xs-8" id="gridStyle"
ng-model="vm.gridStyle"
ng-change="vm.gridStyleUpdated()"
full-select datarepo="vm.gridStyles">
</select>
</fieldset>
And here is what the directive looks like:
app.directive('fullSelect', function ($compile) {
return {
restrict: 'A',
scope: { datarepo: "=datarepo" },
replace: true,
link: function (scope, element, attrs) {
angular.forEach(scope.datarepo, function (value, key) {
var opt;
var display = "";
for (var idx = 0; idx < value.level; idx++) {
display += " ";
}
display += value.label;
if (value.type === "Option") {
opt = angular.element('<option value="' + value.value + '">' + display + '</option>');
}
else {
opt = angular.element('<optgroup label="' + display + '"></optgroup>');
}
element.append($compile(opt)(scope));
});
}
}
});
Now I just need to make sure it performs properly, but so far it looks good. It gives me my desired end result where my incoming object array contains values, labels and misc info such as a level property to indicate where that item should be indented within the dropdown list, etc. I can now have any number of option and optgroup children embedded within the dropdown list indented as needed. You cannot nest multiple optgroup elements, but I visually handle that with the level property which adds spaces to the text.