| OLD | NEW |
| 1 part of angular.directive; | 1 part of angular.directive; |
| 2 | 2 |
| 3 /** | 3 /** |
| 4 * The `ngClass` allows you to set CSS classes on HTML an element, dynamically, | 4 * The `ngClass` allows you to set CSS classes on HTML an element, dynamically, |
| 5 * by databinding an expression that represents all classes to be added. | 5 * by databinding an expression that represents all classes to be added. |
| 6 * | 6 * |
| 7 * The directive won't add duplicate classes if a particular class was | 7 * The directive won't add duplicate classes if a particular class was |
| 8 * already set. | 8 * already set. |
| 9 * | 9 * |
| 10 * When the expression changes, the previously added classes are removed and | 10 * When the expression changes, the previously added classes are removed and |
| 11 * only then the new classes are added. | 11 * only then the new classes are added. |
| 12 * | 12 * |
| 13 * The result of the expression evaluation can be a string representing space | 13 * The result of the expression evaluation can be a string representing space |
| 14 * delimited class names, an array, or a map of class names to boolean values. | 14 * delimited class names, an array, or a map of class names to boolean values. |
| 15 * In the case of a map, the names of the properties whose values are truthy | 15 * In the case of a map, the names of the properties whose values are truthy |
| 16 * will be added as css classes to the element. | 16 * will be added as css classes to the element. |
| 17 * | 17 * |
| 18 * ##Examples | 18 * ##Examples |
| 19 * | 19 * |
| 20 * index.html: | 20 * index.html: |
| 21 * | 21 * |
| 22 * <!-- | 22 * <p ng-class="{strike: strike, bold: bold, red: red}">Map Syntax Example</
p> |
| 23 * The map syntax: | |
| 24 * | |
| 25 * ng-class="{key1: value1, key2: value2, ...}" | |
| 26 * | |
| 27 * results in only adding CSS classes represented by the map keys when | |
| 28 * the corresponding value expressions are truthy. | |
| 29 * | |
| 30 * To use a css class that contains a hyphen (such as line-through in this | |
| 31 * example), you should quote the name to make it a valid map key. You | |
| 32 * may, of course, quote all the map keys for consistency. | |
| 33 * --> | |
| 34 * <p ng-class="{'line-through': strike, bold: bold, red: red}">Map Syntax E
xample</p> | |
| 35 * <input type="checkbox" ng-model="bold"> bold | 23 * <input type="checkbox" ng-model="bold"> bold |
| 36 * <input type="checkbox" ng-model="strike"> strike | 24 * <input type="checkbox" ng-model="strike"> strike |
| 37 * <input type="checkbox" ng-model="red"> red | 25 * <input type="checkbox" ng-model="red"> red |
| 38 * <hr> | 26 * <hr> |
| 39 * | |
| 40 * <p ng-class="style">Using String Syntax</p> | 27 * <p ng-class="style">Using String Syntax</p> |
| 41 * <input type="text" ng-model="style" placeholder="Type: bold strike red"> | 28 * <input type="text" ng-model="style" placeholder="Type: bold strike red"> |
| 42 * <hr> | 29 * <hr> |
| 43 * | |
| 44 * <p ng-class="[style1, style2, style3]">Using Array Syntax</p> | 30 * <p ng-class="[style1, style2, style3]">Using Array Syntax</p> |
| 45 * <input ng-model="style1" placeholder="Type: bold"><br> | 31 * <input ng-model="style1" placeholder="Type: bold"><br> |
| 46 * <input ng-model="style2" placeholder="Type: strike"><br> | 32 * <input ng-model="style2" placeholder="Type: strike"><br> |
| 47 * <input ng-model="style3" placeholder="Type: red"><br> | 33 * <input ng-model="style3" placeholder="Type: red"><br> |
| 48 * | 34 * |
| 49 * style.css: | 35 * style.css: |
| 50 * | 36 * |
| 51 * .strike { | 37 * .strike { |
| 52 * text-decoration: line-through; | 38 * text-decoration: line-through; |
| 53 * } | 39 * } |
| 54 * .line-through { | |
| 55 * text-decoration: line-through; | |
| 56 * } | |
| 57 * .bold { | 40 * .bold { |
| 58 * font-weight: bold; | 41 * font-weight: bold; |
| 59 * } | 42 * } |
| 60 * .red { | 43 * .red { |
| 61 * color: red; | 44 * color: red; |
| 62 * } | 45 * } |
| 63 * | 46 * |
| 64 */ | 47 */ |
| 65 @NgDirective( | 48 @NgDirective( |
| 66 selector: '[ng-class]', | 49 selector: '[ng-class]', |
| 67 map: const {'ng-class': '@valueExpression'}, | 50 map: const {'ng-class': '@valueExpression'}, |
| 68 exportExpressionAttrs: const ['ng-class']) | 51 exportExpressionAttrs: const ['ng-class']) |
| 69 class NgClassDirective extends _NgClassBase { | 52 class NgClassDirective extends _NgClassBase { |
| 70 NgClassDirective(dom.Element element, Scope scope, NodeAttrs attrs, AstParser
parser) | 53 NgClassDirective(dom.Element element, Scope scope, NodeAttrs attrs) |
| 71 : super(element, scope, null, attrs, parser); | 54 : super(element, scope, null, attrs); |
| 72 } | 55 } |
| 73 | 56 |
| 74 /** | 57 /** |
| 75 * The `ngClassOdd` and `ngClassEven` directives work exactly as | 58 * The `ngClassOdd` and `ngClassEven` directives work exactly as |
| 76 * {@link ng.directive:ngClass ngClass}, except it works in | 59 * {@link ng.directive:ngClass ngClass}, except it works in |
| 77 * conjunction with `ngRepeat` and takes affect only on odd (even) rows. | 60 * conjunction with `ngRepeat` and takes affect only on odd (even) rows. |
| 78 * | 61 * |
| 79 * This directive can be applied only within a scope of an `ngRepeat`. | 62 * This directive can be applied only within a scope of an `ngRepeat`. |
| 80 * | 63 * |
| 81 * ##Examples | 64 * ##Examples |
| (...skipping 13 matching lines...) Expand all Loading... |
| 95 * } | 78 * } |
| 96 * .even { | 79 * .even { |
| 97 * color: blue; | 80 * color: blue; |
| 98 * } | 81 * } |
| 99 */ | 82 */ |
| 100 @NgDirective( | 83 @NgDirective( |
| 101 selector: '[ng-class-odd]', | 84 selector: '[ng-class-odd]', |
| 102 map: const {'ng-class-odd': '@valueExpression'}, | 85 map: const {'ng-class-odd': '@valueExpression'}, |
| 103 exportExpressionAttrs: const ['ng-class-odd']) | 86 exportExpressionAttrs: const ['ng-class-odd']) |
| 104 class NgClassOddDirective extends _NgClassBase { | 87 class NgClassOddDirective extends _NgClassBase { |
| 105 NgClassOddDirective(dom.Element element, Scope scope, NodeAttrs attrs, AstPars
er parser) | 88 NgClassOddDirective(dom.Element element, Scope scope, NodeAttrs attrs) |
| 106 : super(element, scope, 0, attrs, parser); | 89 : super(element, scope, 0, attrs); |
| 107 } | 90 } |
| 108 | 91 |
| 109 /** | 92 /** |
| 110 * The `ngClassOdd` and `ngClassEven` directives work exactly as | 93 * The `ngClassOdd` and `ngClassEven` directives work exactly as |
| 111 * {@link ng.directive:ngClass ngClass}, except it works in | 94 * {@link ng.directive:ngClass ngClass}, except it works in |
| 112 * conjunction with `ngRepeat` and takes affect only on odd (even) rows. | 95 * conjunction with `ngRepeat` and takes affect only on odd (even) rows. |
| 113 * | 96 * |
| 114 * This directive can be applied only within a scope of an `ngRepeat`. | 97 * This directive can be applied only within a scope of an `ngRepeat`. |
| 115 * | 98 * |
| 116 * ##Examples | 99 * ##Examples |
| (...skipping 13 matching lines...) Expand all Loading... |
| 130 * } | 113 * } |
| 131 * .even { | 114 * .even { |
| 132 * color: blue; | 115 * color: blue; |
| 133 * } | 116 * } |
| 134 */ | 117 */ |
| 135 @NgDirective( | 118 @NgDirective( |
| 136 selector: '[ng-class-even]', | 119 selector: '[ng-class-even]', |
| 137 map: const {'ng-class-even': '@valueExpression'}, | 120 map: const {'ng-class-even': '@valueExpression'}, |
| 138 exportExpressionAttrs: const ['ng-class-even']) | 121 exportExpressionAttrs: const ['ng-class-even']) |
| 139 class NgClassEvenDirective extends _NgClassBase { | 122 class NgClassEvenDirective extends _NgClassBase { |
| 140 NgClassEvenDirective(dom.Element element, Scope scope, NodeAttrs attrs, AstPar
ser parser) | 123 NgClassEvenDirective(dom.Element element, Scope scope, NodeAttrs attrs) |
| 141 : super(element, scope, 1, attrs, parser); | 124 : super(element, scope, 1, attrs); |
| 142 } | 125 } |
| 143 | 126 |
| 144 abstract class _NgClassBase { | 127 abstract class _NgClassBase { |
| 145 final dom.Element element; | 128 final dom.Element element; |
| 146 final Scope scope; | 129 final Scope scope; |
| 147 final int mode; | 130 final int mode; |
| 148 final NodeAttrs nodeAttrs; | 131 final NodeAttrs nodeAttrs; |
| 149 final AstParser _parser; | |
| 150 var previousSet = []; | 132 var previousSet = []; |
| 151 var currentSet = []; | 133 var currentSet = []; |
| 152 | 134 |
| 153 _NgClassBase(this.element, this.scope, this.mode, this.nodeAttrs, this._parser
) { | 135 _NgClassBase(this.element, this.scope, this.mode, this.nodeAttrs) { |
| 154 var prevClass; | 136 var prevClass; |
| 155 | 137 |
| 156 nodeAttrs.observe('class', (String newValue) { | 138 nodeAttrs.observe('class', (String newValue) { |
| 157 if (prevClass != newValue) { | 139 if (prevClass != newValue) { |
| 158 prevClass = newValue; | 140 prevClass = newValue; |
| 159 _handleChange(scope.context[r'$index']); | 141 _handleChange(scope[r'$index']); |
| 160 } | 142 } |
| 161 }); | 143 }); |
| 162 } | 144 } |
| 163 | 145 |
| 164 set valueExpression(currentExpression) { | 146 set valueExpression(currentExpression) { |
| 165 // this should be called only once, so we don't worry about cleaning up | 147 // this should be called only once, so we don't worry about cleaning up |
| 166 // watcher registrations. | 148 // watcher registrations. |
| 167 scope.watch( | 149 scope.$watchCollection(currentExpression, (current) { |
| 168 _parser(currentExpression, collection: true), | 150 currentSet = _flatten(current); |
| 169 (current, _) { | 151 _handleChange(scope[r'$index']); |
| 170 currentSet = _flatten(current); | 152 }); |
| 171 _handleChange(scope.context[r'$index']); | |
| 172 }, | |
| 173 readOnly: true | |
| 174 ); | |
| 175 if (mode != null) { | 153 if (mode != null) { |
| 176 scope.watch(_parser(r'$index'), (index, oldIndex) { | 154 scope.$watch(r'$index', (index, oldIndex) { |
| 177 var mod = index % 2; | 155 var mod = index % 2; |
| 178 if (oldIndex == null || mod != oldIndex % 2) { | 156 if (oldIndex == null || mod != oldIndex % 2) { |
| 179 if (mod == mode) { | 157 if (mod == mode) { |
| 180 element.classes.addAll(currentSet); | 158 element.classes.addAll(currentSet); |
| 181 } else { | 159 } else { |
| 182 element.classes.removeAll(previousSet); | 160 element.classes.removeAll(previousSet); |
| 183 } | 161 } |
| 184 } | 162 } |
| 185 }, readOnly: true); | 163 }); |
| 186 } | 164 } |
| 187 } | 165 } |
| 188 | 166 |
| 189 _handleChange(index) { | 167 _handleChange(index) { |
| 190 if (mode == null || (index != null && index % 2 == mode)) { | 168 if (mode == null || (index != null && index % 2 == mode)) { |
| 191 element.classes..removeAll(previousSet)..addAll(currentSet); | 169 element.classes.removeAll(previousSet); |
| 170 element.classes.addAll(currentSet); |
| 192 } | 171 } |
| 193 | 172 |
| 194 previousSet = currentSet; | 173 previousSet = currentSet; |
| 195 } | 174 } |
| 196 | 175 |
| 197 static List<String> _flatten(classes) { | 176 static List<String> _flatten(classes) { |
| 198 if (classes == null) return []; | 177 if (classes == null) return []; |
| 199 if (classes is CollectionChangeRecord) { | |
| 200 classes = (classes as CollectionChangeRecord).iterable.toList(); | |
| 201 } | |
| 202 if (classes is List) { | 178 if (classes is List) { |
| 203 return classes.where((String e) => e != null && e.isNotEmpty) | 179 return classes; |
| 204 .toList(growable: false); | |
| 205 } | |
| 206 if (classes is MapChangeRecord) { | |
| 207 classes = (classes as MapChangeRecord).map; | |
| 208 } | 180 } |
| 209 if (classes is Map) { | 181 if (classes is Map) { |
| 210 return classes.keys.where((key) => toBool(classes[key])).toList(); | 182 return classes.keys.where((key) => toBool(classes[key])).toList(); |
| 211 } | 183 } |
| 212 if (classes is String) return classes.split(' '); | 184 if (classes is String) { |
| 213 throw 'ng-class expects expression value to be List, Map or String, got $cla
sses'; | 185 return classes.split(' '); |
| 186 } |
| 187 throw 'ng-class expects expression value to be List, Map or String.'; |
| 214 } | 188 } |
| 215 } | 189 } |
| OLD | NEW |