0

This directive is used to determine, if given value is, or is not in datalist associated with it. It works perfectly when I type into the input, but it wont work if the datalist changes due to $digest cycle (adding values to it). If I then update the input, it will function correctly.

app.directive('list', function (){
    return {
        restrict: "A",
        require: "ngModel",
        priority: 100,
        link: function(scope, elem, attr, ngModel){
            var list;

            //For DOM -> model validation
            ngModel.$validators.list = function(value){
                if(!list){
                    var options = document.getElementById(attr.list).options;
                    var list = [];
                    for(var i=options.length-1; i>=0; i--){
                        if(isString(options[i].getAttribute("valid"))){
                            if(options[i].label){list.push(options[i].label.toLowerCase())}
                            if(options[i].value){list.push(options[i].value.toLowerCase())}
                        }
                    };
                }
                var valid = (value||!value==""?list.indexOf(value.toLowerCase()) !== -1:true);
                return (!list.length)||valid;
            };
        }
    };
});
Akxe
  • 9,694
  • 3
  • 36
  • 71
  • It wouldn't, because there is no trigger (except when you type or other digest event) to re-run the validation. Why are you passing an element id to `list`? Pass the actual VM that built the options - then you could watch it for changes. – New Dev Jan 25 '15 at 15:17
  • To be closest possible to the standard... – Akxe Jan 25 '15 at 17:48
  • What does that mean? Somewhere you have a `select` and you are getting its options directly from DOM. Ideally, the `select` itself should have been driven by a VM property - you should get that property directly, rather than obtaining it from DOM – New Dev Jan 25 '15 at 18:02
  • It is datalist's is... I could potentualy extract it from the ng-repeat... – Akxe Jan 25 '15 at 19:11
  • You don't need to "extract" it from anywhere. You already have it in your controller - pass it to your directive via an isolate scope. – New Dev Jan 25 '15 at 19:15

2 Answers2

0

you would need to watch the list and then trigger ngModel.$validate(); which would then run the validation pipeline...

app.directive('list', function (){
    return {
        restrict: "A",
        require: "ngModel",
        priority: 100,
        link: function(scope, elem, attr, ngModel){
            var list;


        scope.$watch(function () {
            return $parse(attrs.list)(scope);
        }, function () {
            ngModel.$validate();
        });


            //For DOM -> model validation
            ngModel.$validators.list = function(value){
                if(!list){
                    var options = document.getElementById(attr.list).options;
                    var list = [];
                    for(var i=options.length-1; i>=0; i--){
                        if(isString(options[i].getAttribute("valid"))){
                            if(options[i].label){list.push(options[i].label.toLowerCase())}
                            if(options[i].value){list.push(options[i].value.toLowerCase())}
                        }
                    };
                }
                var valid = (value||!value==""?list.indexOf(value.toLowerCase()) !== -1:true);
                return (!list.length)||valid;
            };
        }
    };
});
harishr
  • 17,807
  • 9
  • 78
  • 125
0

You really need to read this SO question about how to "think in Angular".

Somewhere you have a <datalist> whose id you are passing via the list attribute to your directive. I imagine this is close to what you have:

<datalist id="myDatalist">
  <options ng-repeat="item in items" value="{{item.value}}">{{item.label}}</options>
</datalist>

<input ng-model="foo" list="myDatalist">

Don't try to "extract" values from <options> elements or from anywhere else in the DOM. You already have $scope.items (or however you named it) - use that to pass it to your list directive:

.directive("list", function(){
  return {
    // other directive properties,
    scope: {
       list: "="
    },
    require: "ngModel",
    link: function(scope, element, attrs, ngModel){
       scope.$watchCollection("list", function(){
          ngModel.$validate();
          // do whatever else you need in response to a change in list
       });
       // register validators, etc...
    }
  }
});

And then the usage of the directive is as follows:

<input ng-model="foo" list="items">

Not only would this use Angular to watch for changes in the underlying data, it also decouples your logic from the DOM, so if you decide to change the ID later, or use a different element without <option>s, then you directive doesn't change.

Community
  • 1
  • 1
New Dev
  • 48,427
  • 12
  • 87
  • 129