| 
 | 1 | +'use strict';  | 
 | 2 | + | 
 | 3 | + | 
 | 4 | +/**  | 
 | 5 | + * @ngdoc directive  | 
 | 6 | + * @name ngList  | 
 | 7 | + *  | 
 | 8 | + * @description  | 
 | 9 | + * Text input that converts between a delimited string and an array of strings. The default  | 
 | 10 | + * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom  | 
 | 11 | + * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.  | 
 | 12 | + *  | 
 | 13 | + * The behaviour of the directive is affected by the use of the `ngTrim` attribute.  | 
 | 14 | + * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each  | 
 | 15 | + *   list item is respected. This implies that the user of the directive is responsible for  | 
 | 16 | + *   dealing with whitespace but also allows you to use whitespace as a delimiter, such as a  | 
 | 17 | + *   tab or newline character.  | 
 | 18 | + * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected  | 
 | 19 | + *   when joining the list items back together) and whitespace around each list item is stripped  | 
 | 20 | + *   before it is added to the model.  | 
 | 21 | + *  | 
 | 22 | + * ### Example with Validation  | 
 | 23 | + *  | 
 | 24 | + * <example name="ngList-directive" module="listExample">  | 
 | 25 | + *   <file name="app.js">  | 
 | 26 | + *      angular.module('listExample', [])  | 
 | 27 | + *        .controller('ExampleController', ['$scope', function($scope) {  | 
 | 28 | + *          $scope.names = ['morpheus', 'neo', 'trinity'];  | 
 | 29 | + *        }]);  | 
 | 30 | + *   </file>  | 
 | 31 | + *   <file name="index.html">  | 
 | 32 | + *    <form name="myForm" ng-controller="ExampleController">  | 
 | 33 | + *      List: <input name="namesInput" ng-model="names" ng-list required>  | 
 | 34 | + *      <span class="error" ng-show="myForm.namesInput.$error.required">  | 
 | 35 | + *        Required!</span>  | 
 | 36 | + *      <br>  | 
 | 37 | + *      <tt>names = {{names}}</tt><br/>  | 
 | 38 | + *      <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>  | 
 | 39 | + *      <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>  | 
 | 40 | + *      <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>  | 
 | 41 | + *      <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>  | 
 | 42 | + *     </form>  | 
 | 43 | + *   </file>  | 
 | 44 | + *   <file name="protractor.js" type="protractor">  | 
 | 45 | + *     var listInput = element(by.model('names'));  | 
 | 46 | + *     var names = element(by.exactBinding('names'));  | 
 | 47 | + *     var valid = element(by.binding('myForm.namesInput.$valid'));  | 
 | 48 | + *     var error = element(by.css('span.error'));  | 
 | 49 | + *  | 
 | 50 | + *     it('should initialize to model', function() {  | 
 | 51 | + *       expect(names.getText()).toContain('["morpheus","neo","trinity"]');  | 
 | 52 | + *       expect(valid.getText()).toContain('true');  | 
 | 53 | + *       expect(error.getCssValue('display')).toBe('none');  | 
 | 54 | + *     });  | 
 | 55 | + *  | 
 | 56 | + *     it('should be invalid if empty', function() {  | 
 | 57 | + *       listInput.clear();  | 
 | 58 | + *       listInput.sendKeys('');  | 
 | 59 | + *  | 
 | 60 | + *       expect(names.getText()).toContain('');  | 
 | 61 | + *       expect(valid.getText()).toContain('false');  | 
 | 62 | + *       expect(error.getCssValue('display')).not.toBe('none');  | 
 | 63 | + *     });  | 
 | 64 | + *   </file>  | 
 | 65 | + * </example>  | 
 | 66 | + *  | 
 | 67 | + * ### Example - splitting on whitespace  | 
 | 68 | + * <example name="ngList-directive-newlines">  | 
 | 69 | + *   <file name="index.html">  | 
 | 70 | + *    <textarea ng-model="list" ng-list="
" ng-trim="false"></textarea>  | 
 | 71 | + *    <pre>{{ list | json }}</pre>  | 
 | 72 | + *   </file>  | 
 | 73 | + *   <file name="protractor.js" type="protractor">  | 
 | 74 | + *     it("should split the text by newlines", function() {  | 
 | 75 | + *       var listInput = element(by.model('list'));  | 
 | 76 | + *       var output = element(by.binding('list | json'));  | 
 | 77 | + *       listInput.sendKeys('abc\ndef\nghi');  | 
 | 78 | + *       expect(output.getText()).toContain('[\n  "abc",\n  "def",\n  "ghi"\n]');  | 
 | 79 | + *     });  | 
 | 80 | + *   </file>  | 
 | 81 | + * </example>  | 
 | 82 | + *  | 
 | 83 | + * @element input  | 
 | 84 | + * @param {string=} ngList optional delimiter that should be used to split the value.  | 
 | 85 | + */  | 
 | 86 | +var ngListDirective = function() {  | 
 | 87 | +  return {  | 
 | 88 | +    restrict: 'A',  | 
 | 89 | +    priority: 100,  | 
 | 90 | +    require: 'ngModel',  | 
 | 91 | +    link: function(scope, element, attr, ctrl) {  | 
 | 92 | +      // We want to control whitespace trimming so we use this convoluted approach  | 
 | 93 | +      // to access the ngList attribute, which doesn't pre-trim the attribute  | 
 | 94 | +      var ngList = element.attr(attr.$attr.ngList) || ', ';  | 
 | 95 | +      var trimValues = attr.ngTrim !== 'false';  | 
 | 96 | +      var separator = trimValues ? trim(ngList) : ngList;  | 
 | 97 | + | 
 | 98 | +      var parse = function(viewValue) {  | 
 | 99 | +        // If the viewValue is invalid (say required but empty) it will be `undefined`  | 
 | 100 | +        if (isUndefined(viewValue)) return;  | 
 | 101 | + | 
 | 102 | +        var list = [];  | 
 | 103 | + | 
 | 104 | +        if (viewValue) {  | 
 | 105 | +          forEach(viewValue.split(separator), function(value) {  | 
 | 106 | +            if (value) list.push(trimValues ? trim(value) : value);  | 
 | 107 | +          });  | 
 | 108 | +        }  | 
 | 109 | + | 
 | 110 | +        return list;  | 
 | 111 | +      };  | 
 | 112 | + | 
 | 113 | +      ctrl.$parsers.push(parse);  | 
 | 114 | +      ctrl.$formatters.push(function(value) {  | 
 | 115 | +        if (isArray(value)) {  | 
 | 116 | +          return value.join(ngList);  | 
 | 117 | +        }  | 
 | 118 | + | 
 | 119 | +        return undefined;  | 
 | 120 | +      });  | 
 | 121 | + | 
 | 122 | +      // Override the standard $isEmpty because an empty array means the input is empty.  | 
 | 123 | +      ctrl.$isEmpty = function(value) {  | 
 | 124 | +        return !value || !value.length;  | 
 | 125 | +      };  | 
 | 126 | +    }  | 
 | 127 | +  };  | 
 | 128 | +};  | 
0 commit comments