@angular-wave/angular.ts 0.0.1
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.
- package/.eslintignore +1 -0
- package/.eslintrc.cjs +29 -0
- package/.github/workflows/playwright.yml +27 -0
- package/CHANGELOG.md +17974 -0
- package/CODE_OF_CONDUCT.md +3 -0
- package/CONTRIBUTING.md +246 -0
- package/DEVELOPERS.md +488 -0
- package/LICENSE +22 -0
- package/Makefile +31 -0
- package/README.md +115 -0
- package/RELEASE.md +98 -0
- package/SECURITY.md +16 -0
- package/TRIAGING.md +135 -0
- package/css/angular.css +22 -0
- package/dist/angular-ts.cjs.js +36843 -0
- package/dist/angular-ts.esm.js +36841 -0
- package/dist/angular-ts.umd.js +36848 -0
- package/dist/build/angular-animate.js +4272 -0
- package/dist/build/angular-aria.js +426 -0
- package/dist/build/angular-message-format.js +1072 -0
- package/dist/build/angular-messages.js +829 -0
- package/dist/build/angular-mocks.js +3757 -0
- package/dist/build/angular-parse-ext.js +1275 -0
- package/dist/build/angular-resource.js +911 -0
- package/dist/build/angular-route.js +1266 -0
- package/dist/build/angular-sanitize.js +891 -0
- package/dist/build/angular-touch.js +368 -0
- package/dist/build/angular.js +36600 -0
- package/e2e/unit.spec.ts +15 -0
- package/images/android-chrome-192x192.png +0 -0
- package/images/android-chrome-512x512.png +0 -0
- package/images/apple-touch-icon.png +0 -0
- package/images/favicon-16x16.png +0 -0
- package/images/favicon-32x32.png +0 -0
- package/images/favicon.ico +0 -0
- package/images/site.webmanifest +1 -0
- package/index.html +104 -0
- package/package.json +47 -0
- package/playwright.config.ts +78 -0
- package/public/circle.html +1 -0
- package/public/my_child_directive.html +1 -0
- package/public/my_directive.html +1 -0
- package/public/my_other_directive.html +1 -0
- package/public/test.html +1 -0
- package/rollup.config.js +31 -0
- package/src/animations/animateCache.js +55 -0
- package/src/animations/animateChildrenDirective.js +105 -0
- package/src/animations/animateCss.js +1139 -0
- package/src/animations/animateCssDriver.js +291 -0
- package/src/animations/animateJs.js +367 -0
- package/src/animations/animateJsDriver.js +67 -0
- package/src/animations/animateQueue.js +851 -0
- package/src/animations/animation.js +506 -0
- package/src/animations/module.js +779 -0
- package/src/animations/ngAnimateSwap.js +119 -0
- package/src/animations/rafScheduler.js +50 -0
- package/src/animations/shared.js +378 -0
- package/src/constants.js +20 -0
- package/src/core/animate.js +845 -0
- package/src/core/animateCss.js +73 -0
- package/src/core/animateRunner.js +195 -0
- package/src/core/attributes.js +199 -0
- package/src/core/cache.js +45 -0
- package/src/core/compile.js +4727 -0
- package/src/core/controller.js +225 -0
- package/src/core/exceptionHandler.js +63 -0
- package/src/core/filter.js +146 -0
- package/src/core/interpolate.js +442 -0
- package/src/core/interval.js +188 -0
- package/src/core/intervalFactory.js +57 -0
- package/src/core/location.js +1086 -0
- package/src/core/parser/parse.js +2562 -0
- package/src/core/parser/parse.md +13 -0
- package/src/core/q.js +746 -0
- package/src/core/rootScope.js +1596 -0
- package/src/core/sanitizeUri.js +85 -0
- package/src/core/sce.js +1161 -0
- package/src/core/taskTrackerFactory.js +125 -0
- package/src/core/timeout.js +121 -0
- package/src/core/urlUtils.js +187 -0
- package/src/core/utils.js +1349 -0
- package/src/directive/a.js +37 -0
- package/src/directive/attrs.js +283 -0
- package/src/directive/bind.js +51 -0
- package/src/directive/bind.md +142 -0
- package/src/directive/change.js +12 -0
- package/src/directive/change.md +25 -0
- package/src/directive/cloak.js +12 -0
- package/src/directive/cloak.md +24 -0
- package/src/directive/events.js +75 -0
- package/src/directive/events.md +166 -0
- package/src/directive/form.js +725 -0
- package/src/directive/init.js +15 -0
- package/src/directive/init.md +41 -0
- package/src/directive/input.js +1783 -0
- package/src/directive/list.js +46 -0
- package/src/directive/list.md +22 -0
- package/src/directive/ngClass.js +249 -0
- package/src/directive/ngController.js +64 -0
- package/src/directive/ngCsp.js +82 -0
- package/src/directive/ngIf.js +134 -0
- package/src/directive/ngInclude.js +217 -0
- package/src/directive/ngModel.js +1356 -0
- package/src/directive/ngModelOptions.js +509 -0
- package/src/directive/ngOptions.js +670 -0
- package/src/directive/ngRef.js +90 -0
- package/src/directive/ngRepeat.js +650 -0
- package/src/directive/ngShowHide.js +255 -0
- package/src/directive/ngSwitch.js +178 -0
- package/src/directive/ngTransclude.js +98 -0
- package/src/directive/non-bindable.js +11 -0
- package/src/directive/non-bindable.md +17 -0
- package/src/directive/script.js +30 -0
- package/src/directive/select.js +624 -0
- package/src/directive/style.js +25 -0
- package/src/directive/style.md +23 -0
- package/src/directive/validators.js +329 -0
- package/src/exts/aria.js +544 -0
- package/src/exts/messages.js +852 -0
- package/src/filters/filter.js +207 -0
- package/src/filters/filter.md +69 -0
- package/src/filters/filters.js +239 -0
- package/src/filters/json.md +16 -0
- package/src/filters/limit-to.js +43 -0
- package/src/filters/limit-to.md +19 -0
- package/src/filters/order-by.js +183 -0
- package/src/filters/order-by.md +83 -0
- package/src/index.js +13 -0
- package/src/injector.js +1034 -0
- package/src/jqLite.js +1117 -0
- package/src/loader.js +1320 -0
- package/src/public.js +215 -0
- package/src/routeToRegExp.js +41 -0
- package/src/services/anchorScroll.js +135 -0
- package/src/services/browser.js +321 -0
- package/src/services/cacheFactory.js +398 -0
- package/src/services/cookieReader.js +72 -0
- package/src/services/document.js +64 -0
- package/src/services/http.js +1537 -0
- package/src/services/httpBackend.js +206 -0
- package/src/services/log.js +160 -0
- package/src/services/templateRequest.js +139 -0
- package/test/angular.spec.js +2153 -0
- package/test/aria/aria.spec.js +1245 -0
- package/test/binding.spec.js +504 -0
- package/test/build-test.html +14 -0
- package/test/injector.spec.js +2327 -0
- package/test/jasmine/jasmine-5.1.2/boot0.js +65 -0
- package/test/jasmine/jasmine-5.1.2/boot1.js +133 -0
- package/test/jasmine/jasmine-5.1.2/jasmine-html.js +963 -0
- package/test/jasmine/jasmine-5.1.2/jasmine.css +320 -0
- package/test/jasmine/jasmine-5.1.2/jasmine.js +10824 -0
- package/test/jasmine/jasmine-5.1.2/jasmine_favicon.png +0 -0
- package/test/jasmine/jasmine-browser.json +17 -0
- package/test/jasmine/jasmine.json +9 -0
- package/test/jqlite.spec.js +2133 -0
- package/test/loader.spec.js +219 -0
- package/test/messages/messages.spec.js +1146 -0
- package/test/min-err.spec.js +174 -0
- package/test/mock-test.html +13 -0
- package/test/module-test.html +15 -0
- package/test/ng/anomate.spec.js +606 -0
- package/test/ng/cache-factor.spec.js +334 -0
- package/test/ng/compile.spec.js +17956 -0
- package/test/ng/controller-provider.spec.js +227 -0
- package/test/ng/cookie-reader.spec.js +98 -0
- package/test/ng/directive/a.spec.js +192 -0
- package/test/ng/directive/bind.spec.js +334 -0
- package/test/ng/directive/boolean.spec.js +136 -0
- package/test/ng/directive/change.spec.js +71 -0
- package/test/ng/directive/class.spec.js +858 -0
- package/test/ng/directive/click.spec.js +38 -0
- package/test/ng/directive/cloak.spec.js +44 -0
- package/test/ng/directive/constoller.spec.js +194 -0
- package/test/ng/directive/element-style.spec.js +92 -0
- package/test/ng/directive/event.spec.js +282 -0
- package/test/ng/directive/form.spec.js +1518 -0
- package/test/ng/directive/href.spec.js +143 -0
- package/test/ng/directive/if.spec.js +402 -0
- package/test/ng/directive/include.spec.js +828 -0
- package/test/ng/directive/init.spec.js +68 -0
- package/test/ng/directive/input.spec.js +3810 -0
- package/test/ng/directive/list.spec.js +170 -0
- package/test/ng/directive/model-options.spec.js +1008 -0
- package/test/ng/directive/model.spec.js +1905 -0
- package/test/ng/directive/non-bindable.spec.js +55 -0
- package/test/ng/directive/options.spec.js +3583 -0
- package/test/ng/directive/ref.spec.js +575 -0
- package/test/ng/directive/repeat.spec.js +1675 -0
- package/test/ng/directive/script.spec.js +52 -0
- package/test/ng/directive/scrset.spec.js +67 -0
- package/test/ng/directive/select.spec.js +2541 -0
- package/test/ng/directive/show-hide.spec.js +253 -0
- package/test/ng/directive/src.spec.js +157 -0
- package/test/ng/directive/style.spec.js +178 -0
- package/test/ng/directive/switch.spec.js +647 -0
- package/test/ng/directive/validators.spec.js +717 -0
- package/test/ng/document.spec.js +52 -0
- package/test/ng/filter/filter.spec.js +714 -0
- package/test/ng/filter/filters.spec.js +35 -0
- package/test/ng/filter/limit-to.spec.js +251 -0
- package/test/ng/filter/order-by.spec.js +891 -0
- package/test/ng/filter.spec.js +149 -0
- package/test/ng/http-backend.spec.js +398 -0
- package/test/ng/http.spec.js +4071 -0
- package/test/ng/interpolate.spec.js +642 -0
- package/test/ng/interval.spec.js +343 -0
- package/test/ng/location.spec.js +3488 -0
- package/test/ng/on.spec.js +229 -0
- package/test/ng/parse.spec.js +4655 -0
- package/test/ng/prop.spec.js +805 -0
- package/test/ng/q.spec.js +2904 -0
- package/test/ng/root-element.spec.js +16 -0
- package/test/ng/sanitize-uri.spec.js +249 -0
- package/test/ng/sce.spec.js +660 -0
- package/test/ng/scope.spec.js +3442 -0
- package/test/ng/template-request.spec.js +236 -0
- package/test/ng/timeout.spec.js +351 -0
- package/test/ng/url-utils.spec.js +156 -0
- package/test/ng/utils.spec.js +144 -0
- package/test/original-test.html +21 -0
- package/test/public.spec.js +34 -0
- package/test/sanitize/bing-html.spec.js +36 -0
- package/test/server/express.js +158 -0
- package/test/test-utils.js +11 -0
- package/tsconfig.json +17 -0
- package/types/angular.d.ts +138 -0
- package/types/global.d.ts +9 -0
- package/types/index.d.ts +2357 -0
- package/types/jqlite.d.ts +558 -0
- package/vite.config.js +14 -0
|
@@ -0,0 +1,624 @@
|
|
|
1
|
+
import { jqLite } from "../jqLite";
|
|
2
|
+
import {
|
|
3
|
+
assertNotHasOwnProperty,
|
|
4
|
+
equals,
|
|
5
|
+
hashKey,
|
|
6
|
+
includes,
|
|
7
|
+
isDefined,
|
|
8
|
+
shallowCopy,
|
|
9
|
+
} from "../core/utils";
|
|
10
|
+
|
|
11
|
+
const noopNgModelController = { $setViewValue: () => {}, $render: () => {} };
|
|
12
|
+
|
|
13
|
+
function setOptionSelectedStatus(optionEl, value) {
|
|
14
|
+
optionEl.prop("selected", value);
|
|
15
|
+
/**
|
|
16
|
+
* When unselecting an option, setting the property to null / false should be enough
|
|
17
|
+
* However, screenreaders might react to the selected attribute instead, see
|
|
18
|
+
* https://github.com/angular/angular.js/issues/14419
|
|
19
|
+
* Note: "selected" is a boolean attr and will be removed when the "value" arg in attr() is false
|
|
20
|
+
* or null
|
|
21
|
+
*/
|
|
22
|
+
optionEl.attr("selected", value);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @ngdoc type
|
|
27
|
+
* @name select.SelectController
|
|
28
|
+
*
|
|
29
|
+
* @description
|
|
30
|
+
* The controller for the {@link ng.select select} directive. The controller exposes
|
|
31
|
+
* a few utility methods that can be used to augment the behavior of a regular or an
|
|
32
|
+
* {@link ng.ngOptions ngOptions} select element.
|
|
33
|
+
*
|
|
34
|
+
*/
|
|
35
|
+
var SelectController = [
|
|
36
|
+
"$element",
|
|
37
|
+
"$scope",
|
|
38
|
+
function ($element, $scope) {
|
|
39
|
+
const self = this;
|
|
40
|
+
const optionsMap = new Map();
|
|
41
|
+
|
|
42
|
+
self.selectValueMap = {}; // Keys are the hashed values, values the original values
|
|
43
|
+
|
|
44
|
+
// If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
|
|
45
|
+
self.ngModelCtrl = noopNgModelController;
|
|
46
|
+
self.multiple = false;
|
|
47
|
+
|
|
48
|
+
// The "unknown" option is one that is prepended to the list if the viewValue
|
|
49
|
+
// does not match any of the options. When it is rendered the value of the unknown
|
|
50
|
+
// option is '? XXX ?' where XXX is the hashKey of the value that is not known.
|
|
51
|
+
//
|
|
52
|
+
// Support: IE 9 only
|
|
53
|
+
// We can't just jqLite('<option>') since jqLite is not smart enough
|
|
54
|
+
// to create it in <select> and IE barfs otherwise.
|
|
55
|
+
self.unknownOption = jqLite(window.document.createElement("option"));
|
|
56
|
+
|
|
57
|
+
// The empty option is an option with the value '' that the application developer can
|
|
58
|
+
// provide inside the select. It is always selectable and indicates that a "null" selection has
|
|
59
|
+
// been made by the user.
|
|
60
|
+
// If the select has an empty option, and the model of the select is set to "undefined" or "null",
|
|
61
|
+
// the empty option is selected.
|
|
62
|
+
// If the model is set to a different unmatched value, the unknown option is rendered and
|
|
63
|
+
// selected, i.e both are present, because a "null" selection and an unknown value are different.
|
|
64
|
+
self.hasEmptyOption = false;
|
|
65
|
+
self.emptyOption = undefined;
|
|
66
|
+
|
|
67
|
+
self.renderUnknownOption = function (val) {
|
|
68
|
+
const unknownVal = self.generateUnknownOptionValue(val);
|
|
69
|
+
self.unknownOption.val(unknownVal);
|
|
70
|
+
$element.prepend(self.unknownOption);
|
|
71
|
+
setOptionSelectedStatus(self.unknownOption, true);
|
|
72
|
+
$element.val(unknownVal);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
self.updateUnknownOption = function (val) {
|
|
76
|
+
const unknownVal = self.generateUnknownOptionValue(val);
|
|
77
|
+
self.unknownOption.val(unknownVal);
|
|
78
|
+
setOptionSelectedStatus(self.unknownOption, true);
|
|
79
|
+
$element.val(unknownVal);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
self.generateUnknownOptionValue = function (val) {
|
|
83
|
+
return `? ${hashKey(val)} ?`;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
self.removeUnknownOption = function () {
|
|
87
|
+
if (self.unknownOption.parent()) self.unknownOption.remove();
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
self.selectEmptyOption = function () {
|
|
91
|
+
if (self.emptyOption) {
|
|
92
|
+
$element.val("");
|
|
93
|
+
setOptionSelectedStatus(self.emptyOption, true);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
self.unselectEmptyOption = function () {
|
|
98
|
+
if (self.hasEmptyOption) {
|
|
99
|
+
setOptionSelectedStatus(self.emptyOption, false);
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
$scope.$on("$destroy", () => {
|
|
104
|
+
// disable unknown option so that we don't do work when the whole select is being destroyed
|
|
105
|
+
self.renderUnknownOption = () => {};
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Read the value of the select control, the implementation of this changes depending
|
|
109
|
+
// upon whether the select can have multiple values and whether ngOptions is at work.
|
|
110
|
+
self.readValue = function readSingleValue() {
|
|
111
|
+
const val = $element.val();
|
|
112
|
+
// ngValue added option values are stored in the selectValueMap, normal interpolations are not
|
|
113
|
+
const realVal =
|
|
114
|
+
val in self.selectValueMap ? self.selectValueMap[val] : val;
|
|
115
|
+
|
|
116
|
+
if (self.hasOption(realVal)) {
|
|
117
|
+
return realVal;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return null;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// Write the value to the select control, the implementation of this changes depending
|
|
124
|
+
// upon whether the select can have multiple values and whether ngOptions is at work.
|
|
125
|
+
self.writeValue = function writeSingleValue(value) {
|
|
126
|
+
// Make sure to remove the selected attribute from the previously selected option
|
|
127
|
+
// Otherwise, screen readers might get confused
|
|
128
|
+
const currentlySelectedOption =
|
|
129
|
+
$element[0].options[$element[0].selectedIndex];
|
|
130
|
+
if (currentlySelectedOption)
|
|
131
|
+
setOptionSelectedStatus(jqLite(currentlySelectedOption), false);
|
|
132
|
+
|
|
133
|
+
if (self.hasOption(value)) {
|
|
134
|
+
self.removeUnknownOption();
|
|
135
|
+
|
|
136
|
+
const hashedVal = hashKey(value);
|
|
137
|
+
$element.val(hashedVal in self.selectValueMap ? hashedVal : value);
|
|
138
|
+
|
|
139
|
+
// Set selected attribute and property on selected option for screen readers
|
|
140
|
+
const selectedOption = $element[0].options[$element[0].selectedIndex];
|
|
141
|
+
setOptionSelectedStatus(jqLite(selectedOption), true);
|
|
142
|
+
} else {
|
|
143
|
+
self.selectUnknownOrEmptyOption(value);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
// Tell the select control that an option, with the given value, has been added
|
|
148
|
+
self.addOption = function (value, element) {
|
|
149
|
+
// Skip comment nodes, as they only pollute the `optionsMap`
|
|
150
|
+
if (element[0].nodeType === Node.COMMENT_NODE) return;
|
|
151
|
+
|
|
152
|
+
assertNotHasOwnProperty(value, '"option value"');
|
|
153
|
+
if (value === "") {
|
|
154
|
+
self.hasEmptyOption = true;
|
|
155
|
+
self.emptyOption = element;
|
|
156
|
+
}
|
|
157
|
+
const count = optionsMap.get(value) || 0;
|
|
158
|
+
optionsMap.set(value, count + 1);
|
|
159
|
+
// Only render at the end of a digest. This improves render performance when many options
|
|
160
|
+
// are added during a digest and ensures all relevant options are correctly marked as selected
|
|
161
|
+
scheduleRender();
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// Tell the select control that an option, with the given value, has been removed
|
|
165
|
+
self.removeOption = function (value) {
|
|
166
|
+
const count = optionsMap.get(value);
|
|
167
|
+
if (count) {
|
|
168
|
+
if (count === 1) {
|
|
169
|
+
optionsMap.delete(value);
|
|
170
|
+
if (value === "") {
|
|
171
|
+
self.hasEmptyOption = false;
|
|
172
|
+
self.emptyOption = undefined;
|
|
173
|
+
}
|
|
174
|
+
} else {
|
|
175
|
+
optionsMap.set(value, count - 1);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
// Check whether the select control has an option matching the given value
|
|
181
|
+
self.hasOption = function (value) {
|
|
182
|
+
return !!optionsMap.get(value);
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* @ngdoc method
|
|
187
|
+
* @name select.SelectController#$hasEmptyOption
|
|
188
|
+
*
|
|
189
|
+
* @description
|
|
190
|
+
*
|
|
191
|
+
* Returns `true` if the select element currently has an empty option
|
|
192
|
+
* element, i.e. an option that signifies that the select is empty / the selection is null.
|
|
193
|
+
*
|
|
194
|
+
*/
|
|
195
|
+
self.$hasEmptyOption = function () {
|
|
196
|
+
return self.hasEmptyOption;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* @ngdoc method
|
|
201
|
+
* @name select.SelectController#$isUnknownOptionSelected
|
|
202
|
+
*
|
|
203
|
+
* @description
|
|
204
|
+
*
|
|
205
|
+
* Returns `true` if the select element's unknown option is selected. The unknown option is added
|
|
206
|
+
* and automatically selected whenever the select model doesn't match any option.
|
|
207
|
+
*
|
|
208
|
+
*/
|
|
209
|
+
self.$isUnknownOptionSelected = function () {
|
|
210
|
+
// Presence of the unknown option means it is selected
|
|
211
|
+
return $element[0].options[0] === self.unknownOption[0];
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* @ngdoc method
|
|
216
|
+
* @name select.SelectController#$isEmptyOptionSelected
|
|
217
|
+
*
|
|
218
|
+
* @description
|
|
219
|
+
*
|
|
220
|
+
* Returns `true` if the select element has an empty option and this empty option is currently
|
|
221
|
+
* selected. Returns `false` if the select element has no empty option or it is not selected.
|
|
222
|
+
*
|
|
223
|
+
*/
|
|
224
|
+
self.$isEmptyOptionSelected = function () {
|
|
225
|
+
return (
|
|
226
|
+
self.hasEmptyOption &&
|
|
227
|
+
$element[0].options[$element[0].selectedIndex] === self.emptyOption[0]
|
|
228
|
+
);
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
self.selectUnknownOrEmptyOption = function (value) {
|
|
232
|
+
if (value == null && self.emptyOption) {
|
|
233
|
+
self.removeUnknownOption();
|
|
234
|
+
self.selectEmptyOption();
|
|
235
|
+
} else if (self.unknownOption.parent().length) {
|
|
236
|
+
self.updateUnknownOption(value);
|
|
237
|
+
} else {
|
|
238
|
+
self.renderUnknownOption(value);
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
let renderScheduled = false;
|
|
243
|
+
function scheduleRender() {
|
|
244
|
+
if (renderScheduled) return;
|
|
245
|
+
renderScheduled = true;
|
|
246
|
+
$scope.$$postDigest(() => {
|
|
247
|
+
renderScheduled = false;
|
|
248
|
+
self.ngModelCtrl.$render();
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
let updateScheduled = false;
|
|
253
|
+
function scheduleViewValueUpdate(renderAfter) {
|
|
254
|
+
if (updateScheduled) return;
|
|
255
|
+
|
|
256
|
+
updateScheduled = true;
|
|
257
|
+
|
|
258
|
+
$scope.$$postDigest(() => {
|
|
259
|
+
if ($scope.$$destroyed) return;
|
|
260
|
+
|
|
261
|
+
updateScheduled = false;
|
|
262
|
+
self.ngModelCtrl.$setViewValue(self.readValue());
|
|
263
|
+
if (renderAfter) self.ngModelCtrl.$render();
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
self.registerOption = function (
|
|
268
|
+
optionScope,
|
|
269
|
+
optionElement,
|
|
270
|
+
optionAttrs,
|
|
271
|
+
interpolateValueFn,
|
|
272
|
+
interpolateTextFn,
|
|
273
|
+
) {
|
|
274
|
+
let oldVal;
|
|
275
|
+
let hashedVal;
|
|
276
|
+
if (optionAttrs.$attr.ngValue) {
|
|
277
|
+
// The value attribute is set by ngValue
|
|
278
|
+
|
|
279
|
+
optionAttrs.$observe("value", (newVal) => {
|
|
280
|
+
let removal;
|
|
281
|
+
const previouslySelected = optionElement.prop("selected");
|
|
282
|
+
|
|
283
|
+
if (isDefined(hashedVal)) {
|
|
284
|
+
self.removeOption(oldVal);
|
|
285
|
+
delete self.selectValueMap[hashedVal];
|
|
286
|
+
removal = true;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
hashedVal = hashKey(newVal);
|
|
290
|
+
oldVal = newVal;
|
|
291
|
+
self.selectValueMap[hashedVal] = newVal;
|
|
292
|
+
self.addOption(newVal, optionElement);
|
|
293
|
+
// Set the attribute directly instead of using optionAttrs.$set - this stops the observer
|
|
294
|
+
// from firing a second time. Other $observers on value will also get the result of the
|
|
295
|
+
// ngValue expression, not the hashed value
|
|
296
|
+
optionElement.attr("value", hashedVal);
|
|
297
|
+
|
|
298
|
+
if (removal && previouslySelected) {
|
|
299
|
+
scheduleViewValueUpdate();
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
} else if (interpolateValueFn) {
|
|
303
|
+
// The value attribute is interpolated
|
|
304
|
+
optionAttrs.$observe("value", (newVal) => {
|
|
305
|
+
// This method is overwritten in ngOptions and has side-effects!
|
|
306
|
+
self.readValue();
|
|
307
|
+
|
|
308
|
+
let removal;
|
|
309
|
+
const previouslySelected = optionElement.prop("selected");
|
|
310
|
+
|
|
311
|
+
if (isDefined(oldVal)) {
|
|
312
|
+
self.removeOption(oldVal);
|
|
313
|
+
removal = true;
|
|
314
|
+
}
|
|
315
|
+
oldVal = newVal;
|
|
316
|
+
self.addOption(newVal, optionElement);
|
|
317
|
+
|
|
318
|
+
if (removal && previouslySelected) {
|
|
319
|
+
scheduleViewValueUpdate();
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
} else if (interpolateTextFn) {
|
|
323
|
+
// The text content is interpolated
|
|
324
|
+
optionScope.$watch(interpolateTextFn, (newVal, oldVal) => {
|
|
325
|
+
optionAttrs.$set("value", newVal);
|
|
326
|
+
const previouslySelected = optionElement.prop("selected");
|
|
327
|
+
if (oldVal !== newVal) {
|
|
328
|
+
self.removeOption(oldVal);
|
|
329
|
+
}
|
|
330
|
+
self.addOption(newVal, optionElement);
|
|
331
|
+
|
|
332
|
+
if (oldVal && previouslySelected) {
|
|
333
|
+
scheduleViewValueUpdate();
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
} else {
|
|
337
|
+
// The value attribute is static
|
|
338
|
+
self.addOption(optionAttrs.value, optionElement);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
optionAttrs.$observe("disabled", (newVal) => {
|
|
342
|
+
// Since model updates will also select disabled options (like ngOptions),
|
|
343
|
+
// we only have to handle options becoming disabled, not enabled
|
|
344
|
+
|
|
345
|
+
if (newVal === "true" || (newVal && optionElement.prop("selected"))) {
|
|
346
|
+
if (self.multiple) {
|
|
347
|
+
scheduleViewValueUpdate(true);
|
|
348
|
+
} else {
|
|
349
|
+
self.ngModelCtrl.$setViewValue(null);
|
|
350
|
+
self.ngModelCtrl.$render();
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
optionElement.on("$destroy", () => {
|
|
356
|
+
const currentValue = self.readValue();
|
|
357
|
+
const removeValue = optionAttrs.value;
|
|
358
|
+
|
|
359
|
+
self.removeOption(removeValue);
|
|
360
|
+
scheduleRender();
|
|
361
|
+
|
|
362
|
+
if (
|
|
363
|
+
(self.multiple &&
|
|
364
|
+
currentValue &&
|
|
365
|
+
currentValue.indexOf(removeValue) !== -1) ||
|
|
366
|
+
currentValue === removeValue
|
|
367
|
+
) {
|
|
368
|
+
// When multiple (selected) options are destroyed at the same time, we don't want
|
|
369
|
+
// to run a model update for each of them. Instead, run a single update in the $$postDigest
|
|
370
|
+
scheduleViewValueUpdate(true);
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
};
|
|
374
|
+
},
|
|
375
|
+
];
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* @ngdoc directive
|
|
379
|
+
* @name select
|
|
380
|
+
* @restrict E
|
|
381
|
+
*
|
|
382
|
+
* @description
|
|
383
|
+
* HTML `select` element with AngularJS data-binding.
|
|
384
|
+
*
|
|
385
|
+
* The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
|
|
386
|
+
* between the scope and the `<select>` control (including setting default values).
|
|
387
|
+
* It also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
|
|
388
|
+
* {@link ngOptions `ngOptions`} directives.
|
|
389
|
+
*
|
|
390
|
+
* When an item in the `<select>` menu is selected, the value of the selected option will be bound
|
|
391
|
+
* to the model identified by the `ngModel` directive. With static or repeated options, this is
|
|
392
|
+
* the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
|
|
393
|
+
* Value and textContent can be interpolated.
|
|
394
|
+
*
|
|
395
|
+
* The {@link select.SelectController select controller} exposes utility functions that can be used
|
|
396
|
+
* to manipulate the select's behavior.
|
|
397
|
+
*
|
|
398
|
+
* ## Matching model and option values
|
|
399
|
+
*
|
|
400
|
+
* In general, the match between the model and an option is evaluated by strictly comparing the model
|
|
401
|
+
* value against the value of the available options.
|
|
402
|
+
*
|
|
403
|
+
* If you are setting the option value with the option's `value` attribute, or textContent, the
|
|
404
|
+
* value will always be a `string` which means that the model value must also be a string.
|
|
405
|
+
* Otherwise the `select` directive cannot match them correctly.
|
|
406
|
+
*
|
|
407
|
+
* To bind the model to a non-string value, you can use one of the following strategies:
|
|
408
|
+
* - the {@link ng.ngOptions `ngOptions`} directive
|
|
409
|
+
* ({@link ng.select#using-select-with-ngoptions-and-setting-a-default-value})
|
|
410
|
+
* - the {@link ng.ngValue `ngValue`} directive, which allows arbitrary expressions to be
|
|
411
|
+
* option values ({@link ng.select#using-ngvalue-to-bind-the-model-to-an-array-of-objects Example})
|
|
412
|
+
* - model $parsers / $formatters to convert the string value
|
|
413
|
+
* ({@link ng.select#binding-select-to-a-non-string-value-via-ngmodel-parsing-formatting Example})
|
|
414
|
+
*
|
|
415
|
+
* If the viewValue of `ngModel` does not match any of the options, then the control
|
|
416
|
+
* will automatically add an "unknown" option, which it then removes when the mismatch is resolved.
|
|
417
|
+
*
|
|
418
|
+
* Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
|
|
419
|
+
* be nested into the `<select>` element. This element will then represent the `null` or "not selected"
|
|
420
|
+
* option. See example below for demonstration.
|
|
421
|
+
*
|
|
422
|
+
* ## Choosing between `ngRepeat` and `ngOptions`
|
|
423
|
+
*
|
|
424
|
+
* In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
|
|
425
|
+
* ngOptions} to achieve a similar result. However, `ngOptions` provides some benefits:
|
|
426
|
+
* - more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
|
|
427
|
+
* comprehension expression
|
|
428
|
+
* - reduced memory consumption by not creating a new scope for each repeated instance
|
|
429
|
+
* - increased render speed by creating the options in a documentFragment instead of individually
|
|
430
|
+
*
|
|
431
|
+
* Specifically, select with repeated options slows down significantly starting at 2000 options in
|
|
432
|
+
* Chrome and Internet Explorer / Edge.
|
|
433
|
+
*
|
|
434
|
+
*
|
|
435
|
+
* @param {string} ngModel Assignable AngularJS expression to data-bind to.
|
|
436
|
+
* @param {string=} name Property name of the form under which the control is published.
|
|
437
|
+
* @param {string=} multiple Allows multiple options to be selected. The selected values will be
|
|
438
|
+
* bound to the model as an array.
|
|
439
|
+
* @param {string=} required Sets `required` validation error key if the value is not entered.
|
|
440
|
+
* @param {string=} ngRequired Adds required attribute and required validation constraint to
|
|
441
|
+
* the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
|
|
442
|
+
* when you want to data-bind to the required attribute.
|
|
443
|
+
* @param {string=} ngChange AngularJS expression to be executed when selected option(s) changes due to user
|
|
444
|
+
* interaction with the select element.
|
|
445
|
+
* @param {string=} ngOptions sets the options that the select is populated with and defines what is
|
|
446
|
+
* set on the model on selection. See {@link ngOptions `ngOptions`}.
|
|
447
|
+
* @param {string=} ngAttrSize sets the size of the select element dynamically. Uses the
|
|
448
|
+
* {@link guide/interpolation#-ngattr-for-binding-to-arbitrary-attributes ngAttr} directive.
|
|
449
|
+
*
|
|
450
|
+
*/
|
|
451
|
+
export const selectDirective = function () {
|
|
452
|
+
return {
|
|
453
|
+
restrict: "E",
|
|
454
|
+
require: ["select", "?ngModel"],
|
|
455
|
+
controller: SelectController,
|
|
456
|
+
priority: 1,
|
|
457
|
+
link: {
|
|
458
|
+
pre: selectPreLink,
|
|
459
|
+
post: selectPostLink,
|
|
460
|
+
},
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
function selectPreLink(scope, element, attr, ctrls) {
|
|
464
|
+
const selectCtrl = ctrls[0];
|
|
465
|
+
const ngModelCtrl = ctrls[1];
|
|
466
|
+
|
|
467
|
+
// if ngModel is not defined, we don't need to do anything but set the registerOption
|
|
468
|
+
// function to noop, so options don't get added internally
|
|
469
|
+
if (!ngModelCtrl) {
|
|
470
|
+
selectCtrl.registerOption = () => {};
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
selectCtrl.ngModelCtrl = ngModelCtrl;
|
|
475
|
+
|
|
476
|
+
// When the selected item(s) changes we delegate getting the value of the select control
|
|
477
|
+
// to the `readValue` method, which can be changed if the select can have multiple
|
|
478
|
+
// selected values or if the options are being generated by `ngOptions`
|
|
479
|
+
element.on("change", () => {
|
|
480
|
+
selectCtrl.removeUnknownOption();
|
|
481
|
+
scope.$apply(() => {
|
|
482
|
+
ngModelCtrl.$setViewValue(selectCtrl.readValue());
|
|
483
|
+
});
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
// If the select allows multiple values then we need to modify how we read and write
|
|
487
|
+
// values from and to the control; also what it means for the value to be empty and
|
|
488
|
+
// we have to add an extra watch since ngModel doesn't work well with arrays - it
|
|
489
|
+
// doesn't trigger rendering if only an item in the array changes.
|
|
490
|
+
if (attr.multiple) {
|
|
491
|
+
selectCtrl.multiple = true;
|
|
492
|
+
|
|
493
|
+
// Read value now needs to check each option to see if it is selected
|
|
494
|
+
selectCtrl.readValue = function readMultipleValue() {
|
|
495
|
+
const array = [];
|
|
496
|
+
Array.from(element[0].getElementsByTagName("option")).forEach(
|
|
497
|
+
(option) => {
|
|
498
|
+
if (option.selected && !option.disabled) {
|
|
499
|
+
const val = option.value;
|
|
500
|
+
array.push(
|
|
501
|
+
val in selectCtrl.selectValueMap
|
|
502
|
+
? selectCtrl.selectValueMap[val]
|
|
503
|
+
: val,
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
},
|
|
507
|
+
);
|
|
508
|
+
return array;
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
// Write value now needs to set the selected property of each matching option
|
|
512
|
+
selectCtrl.writeValue = function writeMultipleValue(value) {
|
|
513
|
+
Array.from(element[0].getElementsByTagName("option")).forEach(
|
|
514
|
+
(option) => {
|
|
515
|
+
const shouldBeSelected =
|
|
516
|
+
!!value &&
|
|
517
|
+
(includes(value, option.value) ||
|
|
518
|
+
includes(value, selectCtrl.selectValueMap[option.value]));
|
|
519
|
+
const currentlySelected = option.selected;
|
|
520
|
+
|
|
521
|
+
// Support: IE 9-11 only, Edge 12-15+
|
|
522
|
+
// In IE and Edge adding options to the selection via shift+click/UP/DOWN
|
|
523
|
+
// will de-select already selected options if "selected" on those options was set
|
|
524
|
+
// more than once (i.e. when the options were already selected)
|
|
525
|
+
// So we only modify the selected property if necessary.
|
|
526
|
+
// Note: this behavior cannot be replicated via unit tests because it only shows in the
|
|
527
|
+
// actual user interface.
|
|
528
|
+
if (shouldBeSelected !== currentlySelected) {
|
|
529
|
+
setOptionSelectedStatus(jqLite(option), shouldBeSelected);
|
|
530
|
+
}
|
|
531
|
+
},
|
|
532
|
+
);
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
// we have to do it on each watch since ngModel watches reference, but
|
|
536
|
+
// we need to work of an array, so we need to see if anything was inserted/removed
|
|
537
|
+
let lastView;
|
|
538
|
+
let lastViewRef = NaN;
|
|
539
|
+
scope.$watch(() => {
|
|
540
|
+
if (
|
|
541
|
+
lastViewRef === ngModelCtrl.$viewValue &&
|
|
542
|
+
!equals(lastView, ngModelCtrl.$viewValue)
|
|
543
|
+
) {
|
|
544
|
+
lastView = shallowCopy(ngModelCtrl.$viewValue);
|
|
545
|
+
ngModelCtrl.$render();
|
|
546
|
+
}
|
|
547
|
+
lastViewRef = ngModelCtrl.$viewValue;
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
// If we are a multiple select then value is now a collection
|
|
551
|
+
// so the meaning of $isEmpty changes
|
|
552
|
+
ngModelCtrl.$isEmpty = function (value) {
|
|
553
|
+
return !value || value.length === 0;
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
function selectPostLink(scope, element, attrs, ctrls) {
|
|
559
|
+
// if ngModel is not defined, we don't need to do anything
|
|
560
|
+
const ngModelCtrl = ctrls[1];
|
|
561
|
+
if (!ngModelCtrl) return;
|
|
562
|
+
|
|
563
|
+
const selectCtrl = ctrls[0];
|
|
564
|
+
|
|
565
|
+
// We delegate rendering to the `writeValue` method, which can be changed
|
|
566
|
+
// if the select can have multiple selected values or if the options are being
|
|
567
|
+
// generated by `ngOptions`.
|
|
568
|
+
// This must be done in the postLink fn to prevent $render to be called before
|
|
569
|
+
// all nodes have been linked correctly.
|
|
570
|
+
ngModelCtrl.$render = function () {
|
|
571
|
+
selectCtrl.writeValue(ngModelCtrl.$viewValue);
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
// The option directive is purely designed to communicate the existence (or lack of)
|
|
577
|
+
// of dynamically created (and destroyed) option elements to their containing select
|
|
578
|
+
// directive via its controller.
|
|
579
|
+
export const optionDirective = [
|
|
580
|
+
"$interpolate",
|
|
581
|
+
function ($interpolate) {
|
|
582
|
+
return {
|
|
583
|
+
restrict: "E",
|
|
584
|
+
priority: 100,
|
|
585
|
+
compile(element, attr) {
|
|
586
|
+
let interpolateValueFn;
|
|
587
|
+
let interpolateTextFn;
|
|
588
|
+
|
|
589
|
+
if (isDefined(attr.ngValue)) {
|
|
590
|
+
// Will be handled by registerOption
|
|
591
|
+
} else if (isDefined(attr.value)) {
|
|
592
|
+
// If the value attribute is defined, check if it contains an interpolation
|
|
593
|
+
interpolateValueFn = $interpolate(attr.value, true);
|
|
594
|
+
} else {
|
|
595
|
+
// If the value attribute is not defined then we fall back to the
|
|
596
|
+
// text content of the option element, which may be interpolated
|
|
597
|
+
interpolateTextFn = $interpolate(element.text(), true);
|
|
598
|
+
if (!interpolateTextFn) {
|
|
599
|
+
attr.$set("value", element.text());
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
return function (scope, element, attr) {
|
|
604
|
+
// This is an optimization over using ^^ since we don't want to have to search
|
|
605
|
+
// all the way to the root of the DOM for every single option element
|
|
606
|
+
const selectCtrlName = "$selectController";
|
|
607
|
+
const parent = element.parent();
|
|
608
|
+
const selectCtrl =
|
|
609
|
+
parent.data(selectCtrlName) || parent.parent().data(selectCtrlName); // in case we are in optgroup
|
|
610
|
+
|
|
611
|
+
if (selectCtrl) {
|
|
612
|
+
selectCtrl.registerOption(
|
|
613
|
+
scope,
|
|
614
|
+
element,
|
|
615
|
+
attr,
|
|
616
|
+
interpolateValueFn,
|
|
617
|
+
interpolateTextFn,
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
};
|
|
621
|
+
},
|
|
622
|
+
};
|
|
623
|
+
},
|
|
624
|
+
];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @returns {angular.IDirective}
|
|
3
|
+
*/
|
|
4
|
+
export function ngStyleDirective() {
|
|
5
|
+
return {
|
|
6
|
+
restrict: "EA",
|
|
7
|
+
link: (scope, element, attr) => {
|
|
8
|
+
scope.$watchCollection(attr.ngStyle, (newStyles, oldStyles) => {
|
|
9
|
+
if (oldStyles) {
|
|
10
|
+
const oldKeys = Object.keys(oldStyles);
|
|
11
|
+
for (let i = 0, length = oldKeys.length; i < length; i++) {
|
|
12
|
+
element[0].style[oldKeys[i]] = "";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
if (newStyles) {
|
|
16
|
+
const newEntries = Object.entries(newStyles);
|
|
17
|
+
for (let i = 0, length = newEntries.length; i < length; i++) {
|
|
18
|
+
const [key, value] = newEntries[i];
|
|
19
|
+
element[0].style[key] = value;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
## `ngStyle` Directive
|
|
2
|
+
|
|
3
|
+
### Restrict
|
|
4
|
+
|
|
5
|
+
`AC`
|
|
6
|
+
|
|
7
|
+
### Description
|
|
8
|
+
|
|
9
|
+
The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
|
|
10
|
+
|
|
11
|
+
### Known Issue
|
|
12
|
+
|
|
13
|
+
You should not use [interpolation](guide/interpolation) in the value of the `style` attribute when using the `ngStyle` directive on the same element. See [here](guide/interpolation#known-issues) for more info.
|
|
14
|
+
|
|
15
|
+
### Element
|
|
16
|
+
|
|
17
|
+
`ANY`
|
|
18
|
+
|
|
19
|
+
### Parameter
|
|
20
|
+
|
|
21
|
+
- `{string} ngStyle`: [Expression](guide/expression) which evaluates to an object whose keys are CSS style names and values are corresponding values for those CSS keys.
|
|
22
|
+
|
|
23
|
+
Since some CSS style names are not valid keys for an object, they must be quoted. See the 'background-color' style in the example below.
|