Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Extending select directive #13283

Closed
rand0m86 opened this issue Nov 9, 2015 · 7 comments
Closed

Extending select directive #13283

rand0m86 opened this issue Nov 9, 2015 · 7 comments

Comments

@rand0m86
Copy link

rand0m86 commented Nov 9, 2015

I want to write directive that will check for available options in the scope and preselect some option if only one item present inside ngOptions.

For now I wrote something like this:

<select id="provider" name="provider" class="form-control"
        ng-model="foo.provider"
        ng-options="provider.name for provider in providers track by provider.id"
        select-first-if-only-one="providers"
        required>
    <option value="">- Select -</option>
</select>

and my directive:

'use strict';

angular.module('app')
    .directive('selectFirstIfOnlyOne', function() {
        return {
            restrict: 'A',
            require: 'select',
            link: function(scope, elem, attrs, ctrl) {
                scope.$watchCollection(attrs.selectFirstIfOnlyOne, function(values) {
                    if (angular.isDefined(values) && values.length === 1) {
                        scope.$evalAsync(function() {
                            ctrl.ngModelCtrl.$setViewValue(values[0]);
                        });
                    }
                });
            }
        };
    });

And it works. But I want not to pass array values to directive directly but to take them from ngModel or ngOptions.

I found that SelectController doesn't provide methods to get all values from <select>, same for NgModelController.

Same question on StackOverflow.

@Narretz
Copy link
Contributor

Narretz commented Nov 9, 2015

You should set the scope, not the select. Programatic change should always be driven from the scope. I'd say http://stackoverflow.com/a/33618387/787333 is the way to go.

@Narretz Narretz closed this as completed Nov 9, 2015
@rand0m86
Copy link
Author

It's not the way for me. I need use this logic in lots of places so I'd rather have one directive than lots of controllers with duplicates.

@Narretz
Copy link
Contributor

Narretz commented Nov 10, 2015

I don't see why you'd have lots of controllers and duplicates. The logic of setting on the scope from a directive would be the same in all places.

@rand0m86
Copy link
Author

If I'll do your way then for every <select> where I need this behavior (majority of selects in my app) I will write in controllers smth like this:

promise.then(function(items) {
  $scope.items = items;
  $scope.selectedItem = (items.length === 1) ? items[0] : null;
})

This is ugly

Instead I want just to write this:

promise.then(function(items) {
  $scope.items = items;
})

and be sure that I'll have preselected value if there is only one item in array.

@Narretz
Copy link
Contributor

Narretz commented Nov 10, 2015

I still don't see why you can't just assign the scope value in our directive, similar to the example directive you posted.

@rand0m86
Copy link
Author

Are you are talking about something like this?

ngModel.$setViewValue(items[0]);

because I can't hardcode scope variable names inside directive.

If so then I still have my question - how can I get array of items inside custom directive extending angular's select directive without passing reference to it explicitly? I think it's already duplication of code - why should one try to get values from outside of custom directive if it is already bound to <select> that knows which values contained inside of it.

Also I've written a hack inside of scope.$watchCollection:

scope.$evalAsync(function() { ... })

to be sure that all items were already rendered on the DOM. This is because I can't extend ngOptions which is terminal so I need to write my own bicycle.

In a perfect world I would rather write directive like this:

angular.module('app')
    .directive('selectFirstIfOnlyOne', function() {
        return {
            require: ['ngModel', 'ngOptions'],
            link: function(scope, elem, attrs, ctrls) {
                scope.$watch(ctrls[1].options, function(values)) {
                    if (angular.isDefined(values) && values.length === 1) {
                        ctrls[0].$setViewValue(values[0]);
                    }
                });
            }
        };
    });

@Narretz
Copy link
Contributor

Narretz commented Nov 11, 2015

Oh, I was focusing on a different aspect of your directive (that you shouldn't use setViewValue). I see how exposing the options could be useful in that case. There's another issue where that has been proposed.#7417 If you want to open a PR which implements this change, I'll be happy to look at it,

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants