@angular-wave/angular.ts 0.0.67 → 0.0.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/dist/angular-ts.esm.js +2 -2
  2. package/dist/angular-ts.umd.js +2 -2
  3. package/package.json +1 -1
  4. package/src/animations/animate-swap.js +1 -1
  5. package/src/core/animate/animate.js +5 -5
  6. package/src/core/compile/compile.spec.js +2 -2
  7. package/src/core/controller/controller.js +5 -1
  8. package/src/core/cookie-reader.spec.js +1 -1
  9. package/src/core/di/injector.js +1 -1
  10. package/src/core/di/injector.spec.js +35 -35
  11. package/src/core/di/internal-injector.js +1 -1
  12. package/src/core/filter/filter.spec.js +15 -15
  13. package/src/core/interpolate/interpolate.spec.js +29 -29
  14. package/src/core/interval/interval-factory.js +8 -1
  15. package/src/core/interval/interval.js +30 -16
  16. package/src/core/interval/interval.spec.js +1 -1
  17. package/src/core/location/location.js +473 -626
  18. package/src/core/location/location.md +114 -0
  19. package/src/core/location/location.spec.js +2 -77
  20. package/src/core/parser/lexer.spec.js +1 -1
  21. package/src/core/parser/parse.spec.js +1 -1
  22. package/src/core/q/q.js +64 -65
  23. package/src/core/q/q.spec.js +1 -1
  24. package/src/core/sce/sce.js +1 -3
  25. package/src/core/scope/scope.js +3 -6
  26. package/src/core/timeout/timeout.js +5 -21
  27. package/src/core/url-utils/url-utils.js +5 -0
  28. package/src/directive/attrs/boolean.spec.js +1 -1
  29. package/src/directive/bind/bind.js +2 -2
  30. package/src/directive/class/class.js +1 -1
  31. package/src/directive/form/form.js +1 -18
  32. package/src/directive/form/form.spec.js +2 -2
  33. package/src/directive/include/include.js +7 -0
  34. package/src/directive/input/input.js +1 -1
  35. package/src/{exts → directive}/messages/messages.html +4 -1
  36. package/src/directive/messages/messages.js +346 -0
  37. package/src/{exts → directive}/messages/messages.spec.js +7 -11
  38. package/src/{exts → directive}/messages/messages.test.js +1 -1
  39. package/src/directive/model-options/model-options.spec.js +1 -1
  40. package/src/directive/options/options.js +2 -156
  41. package/src/directive/options/options.md +179 -0
  42. package/src/directive/select/select.js +7 -4
  43. package/src/directive/switch/switch.spec.js +1 -1
  44. package/src/exts/aria/aria.spec.js +1 -1
  45. package/src/filters/filter.js +2 -3
  46. package/src/filters/filter.spec.js +1 -1
  47. package/src/filters/filters.spec.js +1 -1
  48. package/src/filters/limit-to.spec.js +2 -2
  49. package/src/loader.js +1 -1
  50. package/src/loader.spec.js +1 -1
  51. package/src/public.js +12 -2
  52. package/src/router/common/glob.spec.js +1 -1
  53. package/src/router/params/param-factory.js +1 -1
  54. package/src/router/params/param.js +54 -54
  55. package/src/router/path/path-utils.js +1 -0
  56. package/src/router/state/state-builder.spec.js +1 -1
  57. package/src/router/template-factory.js +6 -4
  58. package/src/router/url/url-service.js +7 -0
  59. package/src/router/view/view.spec.js +8 -11
  60. package/src/router/view-scroll.js +6 -1
  61. package/src/services/anchor-scroll.html +83 -0
  62. package/src/services/anchor-scroll.js +25 -10
  63. package/src/services/browser.js +1 -1
  64. package/src/services/http/http.spec.js +40 -40
  65. package/src/shared/jqlite/jqlite.js +4 -4
  66. package/src/shared/jqlite/jqlite.spec.js +4 -4
  67. package/src/shared/utils.js +1 -1
  68. package/types/core/interval/interval-factory.d.ts +1 -1
  69. package/types/core/interval/interval.d.ts +4 -0
  70. package/types/core/location/location.d.ts +235 -166
  71. package/types/core/q/q.d.ts +66 -40
  72. package/types/core/scope/scope.d.ts +5 -8
  73. package/types/core/timeout/timeout.d.ts +3 -7
  74. package/types/core/url-utils/url-utils.d.ts +4 -0
  75. package/types/directive/form/form.d.ts +3 -20
  76. package/types/directive/include/include.d.ts +1 -1
  77. package/types/router/params/param-factory.d.ts +1 -1
  78. package/types/router/params/param.d.ts +11 -0
  79. package/types/router/template-factory.d.ts +4 -4
  80. package/types/router/url/url-service.d.ts +9 -9
  81. package/types/router/view-scroll.d.ts +1 -1
  82. package/types/services/anchor-scroll.d.ts +16 -1
  83. package/types/services/browser.d.ts +2 -2
  84. package/types/shared/jqlite/jqlite.d.ts +2 -2
  85. package/src/exts/messages/messages.js +0 -359
  86. package/types/exts/messages/messages.d.ts +0 -1
  87. /package/src/{exts → directive}/messages/messages.md +0 -0
@@ -10,7 +10,10 @@
10
10
  <script src="/jasmine/jasmine-5.1.2/jasmine-html.js"></script>
11
11
  <script src="/jasmine/jasmine-5.1.2/boot0.js"></script>
12
12
  <script src="/jasmine/jasmine-5.1.2/boot1.js"></script>
13
- <script type="module" src="/src/exts/messages/messages.spec.js"></script>
13
+ <script
14
+ type="module"
15
+ src="/src/directive/messages/messages.spec.js"
16
+ ></script>
14
17
  </head>
15
18
  <body>
16
19
  <div id="dummy"></div>
@@ -0,0 +1,346 @@
1
+ import { forEach, isString } from "../../shared/utils";
2
+
3
+ ngMessagesDirective.$inject = ["$animate"];
4
+ export function ngMessagesDirective($animate) {
5
+ const ACTIVE_CLASS = "ng-active";
6
+ const INACTIVE_CLASS = "ng-inactive";
7
+ return {
8
+ require: "ngMessages",
9
+ restrict: "AE",
10
+ controller: [
11
+ "$element",
12
+ "$scope",
13
+ "$attrs",
14
+ function ($element, $scope, $attrs) {
15
+ const ctrl = this;
16
+ let latestKey = 0;
17
+ let nextAttachId = 0;
18
+ this.head = undefined;
19
+ this.default = undefined;
20
+
21
+ this.getAttachId = function getAttachId() {
22
+ return nextAttachId++;
23
+ };
24
+
25
+ const messages = (this.messages = {});
26
+ let renderLater;
27
+ let cachedCollection;
28
+
29
+ this.render = function (collection) {
30
+ collection = collection || {};
31
+
32
+ renderLater = false;
33
+ cachedCollection = collection;
34
+
35
+ // this is true if the attribute is empty or if the attribute value is truthy
36
+ const multiple =
37
+ isAttrTruthy($scope, $attrs.ngMessagesMultiple) ||
38
+ isAttrTruthy($scope, $attrs.multiple);
39
+
40
+ const unmatchedMessages = [];
41
+ const matchedKeys = {};
42
+ let truthyKeys = 0;
43
+ let messageItem = ctrl.head;
44
+ let messageFound = false;
45
+ let totalMessages = 0;
46
+
47
+ // we use != instead of !== to allow for both undefined and null values
48
+ while (messageItem != null) {
49
+ totalMessages++;
50
+ const messageCtrl = messageItem.message;
51
+
52
+ let messageUsed = false;
53
+ if (!messageFound) {
54
+ forEach(collection, (value, key) => {
55
+ if (truthy(value) && !messageUsed) {
56
+ truthyKeys++;
57
+
58
+ if (messageCtrl.test(key)) {
59
+ // this is to prevent the same error name from showing up twice
60
+ if (matchedKeys[key]) return;
61
+ matchedKeys[key] = true;
62
+
63
+ messageUsed = true;
64
+ messageCtrl.attach();
65
+ }
66
+ }
67
+ });
68
+ }
69
+
70
+ if (messageUsed) {
71
+ // unless we want to display multiple messages then we should
72
+ // set a flag here to avoid displaying the next message in the list
73
+ messageFound = !multiple;
74
+ } else {
75
+ unmatchedMessages.push(messageCtrl);
76
+ }
77
+
78
+ messageItem = messageItem.next;
79
+ }
80
+
81
+ forEach(unmatchedMessages, (messageCtrl) => {
82
+ messageCtrl.detach();
83
+ });
84
+
85
+ const messageMatched = unmatchedMessages.length !== totalMessages;
86
+ const attachDefault =
87
+ ctrl.default && !messageMatched && truthyKeys > 0;
88
+
89
+ if (attachDefault) {
90
+ ctrl.default.attach();
91
+ } else if (ctrl.default) {
92
+ ctrl.default.detach();
93
+ }
94
+
95
+ if (messageMatched || attachDefault) {
96
+ $animate.setClass($element, ACTIVE_CLASS, INACTIVE_CLASS);
97
+ } else {
98
+ $animate.setClass($element, INACTIVE_CLASS, ACTIVE_CLASS);
99
+ }
100
+ };
101
+
102
+ $scope.$watchCollection($attrs.ngMessages || $attrs.for, ctrl.render);
103
+
104
+ this.reRender = function () {
105
+ if (!renderLater) {
106
+ renderLater = true;
107
+ $scope.$evalAsync(() => {
108
+ if (renderLater && cachedCollection) {
109
+ ctrl.render(cachedCollection);
110
+ }
111
+ });
112
+ }
113
+ };
114
+
115
+ this.register = function (comment, messageCtrl, isDefault) {
116
+ if (isDefault) {
117
+ ctrl.default = messageCtrl;
118
+ } else {
119
+ const nextKey = latestKey.toString();
120
+ messages[nextKey] = {
121
+ message: messageCtrl,
122
+ };
123
+ insertMessageNode($element[0], comment, nextKey);
124
+ comment.$$ngMessageNode = nextKey;
125
+ latestKey++;
126
+ }
127
+
128
+ ctrl.reRender();
129
+ };
130
+
131
+ this.deregister = function (comment, isDefault) {
132
+ if (isDefault) {
133
+ delete ctrl.default;
134
+ } else {
135
+ const key = comment.$$ngMessageNode;
136
+ delete comment.$$ngMessageNode;
137
+ removeMessageNode($element[0], comment, key);
138
+ delete messages[key];
139
+ }
140
+ ctrl.reRender();
141
+ };
142
+
143
+ function findPreviousMessage(parent, comment) {
144
+ let prevNode = comment;
145
+ const parentLookup = [];
146
+
147
+ while (prevNode && prevNode !== parent) {
148
+ const prevKey = prevNode.$$ngMessageNode;
149
+ if (prevKey && prevKey.length) {
150
+ return messages[prevKey];
151
+ }
152
+
153
+ // dive deeper into the DOM and examine its children for any ngMessage
154
+ // comments that may be in an element that appears deeper in the list
155
+ if (
156
+ prevNode.childNodes.length &&
157
+ parentLookup.indexOf(prevNode) === -1
158
+ ) {
159
+ parentLookup.push(prevNode);
160
+ prevNode = prevNode.childNodes[prevNode.childNodes.length - 1];
161
+ } else if (prevNode.previousSibling) {
162
+ prevNode = prevNode.previousSibling;
163
+ } else {
164
+ prevNode = prevNode.parentNode;
165
+ parentLookup.push(prevNode);
166
+ }
167
+ }
168
+ }
169
+
170
+ function insertMessageNode(parent, comment, key) {
171
+ const messageNode = messages[key];
172
+ if (!ctrl.head) {
173
+ ctrl.head = messageNode;
174
+ } else {
175
+ const match = findPreviousMessage(parent, comment);
176
+ if (match) {
177
+ messageNode.next = match.next;
178
+ match.next = messageNode;
179
+ } else {
180
+ messageNode.next = ctrl.head;
181
+ ctrl.head = messageNode;
182
+ }
183
+ }
184
+ }
185
+
186
+ function removeMessageNode(parent, comment, key) {
187
+ const messageNode = messages[key];
188
+
189
+ // This message node may have already been removed by a call to deregister()
190
+ if (!messageNode) return;
191
+
192
+ const match = findPreviousMessage(parent, comment);
193
+ if (match) {
194
+ match.next = messageNode.next;
195
+ } else {
196
+ ctrl.head = messageNode.next;
197
+ }
198
+ }
199
+ },
200
+ ],
201
+ };
202
+
203
+ function isAttrTruthy(scope, attr) {
204
+ return (
205
+ (isString(attr) && attr.length === 0) || // empty attribute
206
+ truthy(scope.$eval(attr))
207
+ );
208
+ }
209
+
210
+ function truthy(val) {
211
+ return isString(val) ? val.length : !!val;
212
+ }
213
+ }
214
+
215
+ ngMessagesIncludeDirective.$inject = ["$templateRequest", "$compile"];
216
+ export function ngMessagesIncludeDirective($templateRequest, $compile) {
217
+ return {
218
+ restrict: "AE",
219
+ require: "^^ngMessages", // we only require this for validation sake
220
+ link($scope, element, attrs) {
221
+ const src = attrs.ngMessagesInclude || attrs.src;
222
+ $templateRequest(src).then((html) => {
223
+ if ($scope.$$destroyed) return;
224
+ if (isString(html) && !html.trim()) {
225
+ // Empty template - nothing to compile
226
+ } else {
227
+ // Non-empty template - compile and link
228
+ $compile(html)($scope, (contents) => {
229
+ element.after(contents);
230
+ });
231
+ }
232
+ });
233
+ },
234
+ };
235
+ }
236
+
237
+ export const ngMessageDirective = ngMessageDirectiveFactory(false);
238
+ export const ngMessageExpDirective = ngMessageDirectiveFactory(false);
239
+ export const ngMessageDefaultDirective = ngMessageDirectiveFactory(true);
240
+
241
+ function ngMessageDirectiveFactory(isDefault) {
242
+ return [
243
+ "$animate",
244
+ function ($animate) {
245
+ return {
246
+ restrict: "AE",
247
+ transclude: "element",
248
+ priority: 1, // must run before ngBind, otherwise the text is set on the comment
249
+ terminal: true,
250
+ require: "^^ngMessages",
251
+ link(scope, element, attrs, ngMessagesCtrl, $transclude) {
252
+ let commentNode;
253
+ let records;
254
+ let staticExp;
255
+ let dynamicExp;
256
+
257
+ if (!isDefault) {
258
+ commentNode = element[0];
259
+ staticExp = attrs.ngMessage || attrs.when;
260
+ dynamicExp = attrs.ngMessageExp || attrs.whenExp;
261
+
262
+ const assignRecords = function (items) {
263
+ records = items
264
+ ? Array.isArray(items)
265
+ ? items
266
+ : items.split(/[\s,]+/)
267
+ : null;
268
+ ngMessagesCtrl.reRender();
269
+ };
270
+
271
+ if (dynamicExp) {
272
+ assignRecords(scope.$eval(dynamicExp));
273
+ scope.$watchCollection(dynamicExp, assignRecords);
274
+ } else {
275
+ assignRecords(staticExp);
276
+ }
277
+ }
278
+
279
+ let currentElement;
280
+ let messageCtrl;
281
+ ngMessagesCtrl.register(
282
+ commentNode,
283
+ (messageCtrl = {
284
+ test(name) {
285
+ return contains(records, name);
286
+ },
287
+ attach() {
288
+ if (!currentElement) {
289
+ $transclude((elm, newScope) => {
290
+ $animate.enter(elm, null, element);
291
+ currentElement = elm;
292
+
293
+ // Each time we attach this node to a message we get a new id that we can match
294
+ // when we are destroying the node later.
295
+ const $$attachId = (currentElement.$$attachId =
296
+ ngMessagesCtrl.getAttachId());
297
+
298
+ // in the event that the element or a parent element is destroyed
299
+ // by another structural directive then it's time
300
+ // to deregister the message from the controller
301
+ currentElement.on("$destroy", () => {
302
+ // If the message element was removed via a call to `detach` then `currentElement` will be null
303
+ // So this handler only handles cases where something else removed the message element.
304
+ if (
305
+ currentElement &&
306
+ currentElement.$$attachId === $$attachId
307
+ ) {
308
+ ngMessagesCtrl.deregister(commentNode, isDefault);
309
+ messageCtrl.detach();
310
+ }
311
+ newScope.$destroy();
312
+ });
313
+ });
314
+ }
315
+ },
316
+ detach() {
317
+ if (currentElement) {
318
+ const elm = currentElement;
319
+ currentElement = null;
320
+ $animate.leave(elm);
321
+ }
322
+ },
323
+ }),
324
+ isDefault,
325
+ );
326
+
327
+ // We need to ensure that this directive deregisters itself when it no longer exists
328
+ // Normally this is done when the attached element is destroyed; but if this directive
329
+ // gets removed before we attach the message to the DOM there is nothing to watch
330
+ // in which case we must deregister when the containing scope is destroyed.
331
+ scope.$on("$destroy", () => {
332
+ ngMessagesCtrl.deregister(commentNode, isDefault);
333
+ });
334
+ },
335
+ };
336
+ },
337
+ ];
338
+
339
+ function contains(collection, key) {
340
+ if (collection) {
341
+ return Array.isArray(collection)
342
+ ? collection.indexOf(key) >= 0
343
+ : Object.prototype.hasOwnProperty.call(collection, key);
344
+ }
345
+ }
346
+ }
@@ -9,16 +9,13 @@ describe("ngMessages", () => {
9
9
 
10
10
  beforeEach(() => {
11
11
  window.angular = new Angular();
12
- window.angular
13
- .module("app", ["ng", "ngMessages"])
14
- .directive("messageWrap", () => ({
15
- transclude: true,
16
- scope: {
17
- col: "=col",
18
- },
19
- template:
20
- '<div ng-messages="col"><ng-transclude></ng-transclude></div>',
21
- }));
12
+ window.angular.module("app", ["ng"]).directive("messageWrap", () => ({
13
+ transclude: true,
14
+ scope: {
15
+ col: "=col",
16
+ },
17
+ template: '<div ng-messages="col"><ng-transclude></ng-transclude></div>',
18
+ }));
22
19
 
23
20
  createInjector(["app"]).invoke(
24
21
  (_$rootScope_, _$compile_, _$templateCache_) => {
@@ -932,7 +929,6 @@ describe("ngMessages", () => {
932
929
  ' <div ng-message="failed">Your value is that of failure</div>' +
933
930
  "</div>",
934
931
  )($rootScope);
935
-
936
932
  $rootScope.data = {
937
933
  required: true,
938
934
  failed: true,
@@ -1,6 +1,6 @@
1
1
  import { test, expect } from "@playwright/test";
2
2
 
3
- const TEST_URL = "src/exts/messages/messages.html";
3
+ const TEST_URL = "src/directive/messages/messages.html";
4
4
 
5
5
  test("unit tests contain no errors", async ({ page }) => {
6
6
  await page.goto(TEST_URL);
@@ -1,4 +1,4 @@
1
- import { defaultModelOptions } from "../../directive/model-options/model-options";
1
+ import { defaultModelOptions } from "./model-options";
2
2
  import { dealoc, JQLite } from "../../shared/jqlite/jqlite";
3
3
  import { Angular } from "../../loader";
4
4
  import { createInjector } from "../../core/di/injector";
@@ -11,161 +11,6 @@ import {
11
11
 
12
12
  const ngOptionsMinErr = minErr("ngOptions");
13
13
 
14
- /**
15
- * @ngdoc directive
16
- * @name ngOptions
17
- * @restrict A
18
- *
19
- * @description
20
- *
21
- * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
22
- * elements for the `<select>` element using the array or object obtained by evaluating the
23
- * `ngOptions` comprehension expression.
24
- *
25
- * In many cases, {@link ng.directive:ngRepeat ngRepeat} can be used on `<option>` elements instead of
26
- * `ngOptions` to achieve a similar result. However, `ngOptions` provides some benefits:
27
- * - more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
28
- * comprehension expression
29
- * - reduced memory consumption by not creating a new scope for each repeated instance
30
- * - increased render speed by creating the options in a documentFragment instead of individually
31
- *
32
- * When an item in the `<select>` menu is selected, the array element or object property
33
- * represented by the selected option will be bound to the model identified by the `ngModel`
34
- * directive.
35
- *
36
- * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
37
- * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
38
- * option. See example below for demonstration.
39
- *
40
- * ## Complex Models (objects or collections)
41
- *
42
- * By default, `ngModel` watches the model by reference, not value. This is important to know when
43
- * binding the select to a model that is an object or a collection.
44
- *
45
- * One issue occurs if you want to preselect an option. For example, if you set
46
- * the model to an object that is equal to an object in your collection, `ngOptions` won't be able to set the selection,
47
- * because the objects are not identical. So by default, you should always reference the item in your collection
48
- * for preselections, e.g.: `$scope.selected = $scope.collection[3]`.
49
- *
50
- * Another solution is to use a `track by` clause, because then `ngOptions` will track the identity
51
- * of the item not by reference, but by the result of the `track by` expression. For example, if your
52
- * collection items have an id property, you would `track by item.id`.
53
- *
54
- * A different issue with objects or collections is that ngModel won't detect if an object property or
55
- * a collection item changes. For that reason, `ngOptions` additionally watches the model using
56
- * `$watchCollection`, when the expression contains a `track by` clause or the the select has the `multiple` attribute.
57
- * This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection
58
- * has not changed identity, but only a property on the object or an item in the collection changes.
59
- *
60
- * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
61
- * if the model is an array). This means that changing a property deeper than the first level inside the
62
- * object/collection will not trigger a re-rendering.
63
- *
64
- * ## `select` **`as`**
65
- *
66
- * Using `select` **`as`** will bind the result of the `select` expression to the model, but
67
- * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
68
- * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
69
- * is used, the result of that expression will be set as the value of the `option` and `select` elements.
70
- *
71
- *
72
- * ### `select` **`as`** and **`track by`**
73
- *
74
- * <div class="alert alert-warning">
75
- * Be careful when using `select` **`as`** and **`track by`** in the same expression.
76
- * </div>
77
- *
78
- * Given this array of items on the $scope:
79
- *
80
- * ```js
81
- * $scope.items = [{
82
- * id: 1,
83
- * label: 'aLabel',
84
- * subItem: { name: 'aSubItem' }
85
- * }, {
86
- * id: 2,
87
- * label: 'bLabel',
88
- * subItem: { name: 'bSubItem' }
89
- * }];
90
- * ```
91
- *
92
- * This will work:
93
- *
94
- * ```html
95
- * <select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
96
- * ```
97
- * ```js
98
- * $scope.selected = $scope.items[0];
99
- * ```
100
- *
101
- * but this will not work:
102
- *
103
- * ```html
104
- * <select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select>
105
- * ```
106
- * ```js
107
- * $scope.selected = $scope.items[0].subItem;
108
- * ```
109
- *
110
- * In both examples, the **`track by`** expression is applied successfully to each `item` in the
111
- * `items` array. Because the selected option has been set programmatically in the controller, the
112
- * **`track by`** expression is also applied to the `ngModel` value. In the first example, the
113
- * `ngModel` value is `items[0]` and the **`track by`** expression evaluates to `items[0].id` with
114
- * no issue. In the second example, the `ngModel` value is `items[0].subItem` and the **`track by`**
115
- * expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value
116
- * is not matched against any `<option>` and the `<select>` appears as having no selected value.
117
- *
118
- *
119
- * @param {string} ngModel Assignable AngularJS expression to data-bind to.
120
- * @param {comprehension_expression} ngOptions in one of the following forms:
121
- *
122
- * * for array data sources:
123
- * * `label` **`for`** `value` **`in`** `array`
124
- * * `select` **`as`** `label` **`for`** `value` **`in`** `array`
125
- * * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
126
- * * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array`
127
- * * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
128
- * * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
129
- * * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
130
- * (for including a filter with `track by`)
131
- * * for object data sources:
132
- * * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
133
- * * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
134
- * * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
135
- * * `label` **`disable when`** `disable` **`for (`**`key`**`,`** `value`**`) in`** `object`
136
- * * `select` **`as`** `label` **`group by`** `group`
137
- * **`for` `(`**`key`**`,`** `value`**`) in`** `object`
138
- * * `select` **`as`** `label` **`disable when`** `disable`
139
- * **`for` `(`**`key`**`,`** `value`**`) in`** `object`
140
- *
141
- * Where:
142
- *
143
- * * `array` / `object`: an expression which evaluates to an array / object to iterate over.
144
- * * `value`: local variable which will refer to each item in the `array` or each property value
145
- * of `object` during iteration.
146
- * * `key`: local variable which will refer to a property name in `object` during iteration.
147
- * * `label`: The result of this expression will be the label for `<option>` element. The
148
- * `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
149
- * * `select`: The result of this expression will be bound to the model of the parent `<select>`
150
- * element. If not specified, `select` expression will default to `value`.
151
- * * `group`: The result of this expression will be used to group options using the `<optgroup>`
152
- * DOM element.
153
- * * `disable`: The result of this expression will be used to disable the rendered `<option>`
154
- * element. Return `true` to disable.
155
- * * `trackexpr`: Used when working with an array of objects. The result of this expression will be
156
- * used to identify the objects in the array. The `trackexpr` will most likely refer to the
157
- * `value` variable (e.g. `value.propertyName`). With this the selection is preserved
158
- * even when the options are recreated (e.g. reloaded from the server).
159
- * @param {string=} name Property name of the form under which the control is published.
160
- * @param {string=} required The control is considered valid only if value is entered.
161
- * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
162
- * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
163
- * `required` when you want to data-bind to the `required` attribute.
164
- * @param {string=} ngAttrSize sets the size of the select element dynamically. Uses the
165
- * {@link guide/interpolation#-ngattr-for-binding-to-arbitrary-attributes ngAttr} directive.
166
- *
167
- */
168
-
169
14
  const NG_OPTIONS_REGEXP =
170
15
  /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?(?:\s+disable\s+when\s+([\s\S]+?))?\s+for\s+(?:([$\w][$\w]*)|(?:\(\s*([$\w][$\w]*)\s*,\s*([$\w][$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/;
171
16
  // 1: value expression (valueFn)
@@ -618,7 +463,8 @@ export const ngOptionsDirective = [
618
463
 
619
464
  // Update the label on the group element
620
465
  // "null" is special cased because of Safari
621
- groupElement.label =
466
+ /** @type {HTMLOptGroupElement} */
467
+ (groupElement).label =
622
468
  option.group === null ? "null" : option.group;
623
469
 
624
470
  // Store it for use later