0

Can anyone tell me why the model option in the first example is selected, and the second one does not for a plain array:

// Plain old array
vm.owner = ['example', 'example2', 'example3', ...];

Where the model vm.model.address.owner = 2;

// EXAMPLE 1 - Works
// Force index to be a number: using id*1 instead of it being a string:
// and the option ('example3') is selected based on the model value of 2 
// indicating the index
<select id="owner"
        name="owner"
        placeholder="Residential Status"
        ng-model="vm.model.address.owner"
        ng-required="true"
        ng-options="id*1 as owner for (id, owner) in vm.owner">
    <option value="">Select Value</option>
</select>

Attempting to not use a hack and using track by instead index 2 is not selected even though the value is still set in the model.

// EXAMPLE 2 - Doesn't Work
// Markup doesn't show value to be a string: using track by, but the 
// option set in the model doesn't cause the option to be selected it 
// remains as the default with a value of ''
<select id="owner"
        name="owner"
        placeholder="Residential Status"
        ng-model="vm.model.address.owner"
        ng-required="true"
        ng-options="owner for (id, owner) in vm.owner track by id">
    <option value="">Select Value</option>
</select>

I find ngOptions to be super confusing so any explanation or solution for example 2 since it is cleaner and not a hack would be great.

2 Answers 2

0

Didn't find a solution using track by, but the AngularJS docs for Select had a solution using a parser and formatter so I could get away from using the hack in the question. I tweaked it a bit so if the key was a string it will leave it alone, otherwise it converts it, and this seems to work. Any criticisms or issues that I don't see please comment, otherwise hope this helps someone.

(function () {

    'use strict';

    /**
     * Binds a select field to a non-string value via ngModel parsing and formatting,
     * which simply uses these pipelines to convert the string value.
     * @constructor
     * @ngInject
     * ---
     * NOTE: In general matches between a model and an option is evaluated by strict
     * comparison of the model value against the value of the available options.
     * Setting the option value with the option's "value" attribute the value
     * will always be a "string", which means that the model value must also
     * be a string, otherwise the select directive cannot match them
     * reliably.
     */
    function selectConvertKey(_) {

        return {
            require: 'ngModel',
            link: function ($scope, $element, $attrs, $ctrl) {

                var ngModelCtrl = $ctrl;

                // Do nothing if no ng-model
                if (!ngModelCtrl) {
                    return;
                }

                // ---
                // PRIVATE METHODS.
                // ---

                /**
                 * Convert the key to a number if the key is a number.
                 * @param key
                 * @returns {Number}
                 * ---
                 * NOTE: Using Number() instead of parseInt() means that a string
                 * composed of letters and numbers, and start with a number will
                 * not be converted.
                 */
                function selectConvertKeyParser(key) {

                    var keyAsNumber = Number(key);

                    // Check if the key is not a number
                    if(_.isNaN(keyAsNumber)) {
                        return key;
                    }

                    return keyAsNumber;
                }

                /**
                 * Convert the key to a string.
                 * @param key
                 * @returns {string}
                 */
                function selectConvertKeyFormatter(key) {
                    return '' + key;
                }

                // ---
                // MODEL PROPERTIES.
                // ---

                /**
                 * Formatters used to control how the model changes are formatted
                 * in the view, also known as model-to-view conversion.
                 * ---
                 * NOTE: Formatters are not invoked when the model is changed
                 * in the view. They are only triggered if the model changes
                 * in code. So you could type forever into the input, and
                 * the formatter would never be invoked.
                 */
                ngModelCtrl.$formatters.push(selectConvertKeyFormatter);

                /**
                 * Parsers used to control how the view changes read from the
                 * DOM are sanitized/formatted prior to saving them to the
                 * model, and updating the view if required.
                 */
                ngModelCtrl.$parsers.push(selectConvertKeyParser);
            }
        };
    }

    selectConvertKey.$inject = [
        '_'
    ];

    angular
        .module('app')
        .directive('selectConvertKey', selectConvertKey);

})();
Sign up to request clarification or add additional context in comments.

Comments

0

Yes, the problem appears to be that the select is being bound to a non-string value. If you do the following, it would work:

//controller
vm.model.address.owner = "2"

//html
ng-options="id as owner for (id, owner) in vm.owner"

See Angularjs ng-options using number for model does not select initial value.

Also, if you want to leave the model value as a number (2, not "2") you can try this:

ng-options="vm.owner.indexOf(owner) as owner for (id, owner) in vm.owner"

However, that may not be any less "hackish" than your working first example:

ng-options="id*1 as owner for (id, owner) in vm.owner">

See the first answer at AngularJS ng-option get index.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.