@angular-wave/angular.ts 0.0.68 → 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.
- package/dist/angular-ts.esm.js +2 -2
- package/dist/angular-ts.umd.js +2 -2
- package/package.json +1 -1
- package/src/animations/animate-swap.js +1 -1
- package/src/core/animate/animate.js +5 -5
- package/src/core/compile/compile.spec.js +2 -2
- package/src/core/controller/controller.js +5 -1
- package/src/core/cookie-reader.spec.js +1 -1
- package/src/core/di/injector.js +1 -1
- package/src/core/di/injector.spec.js +35 -35
- package/src/core/di/internal-injector.js +1 -1
- package/src/core/filter/filter.spec.js +15 -15
- package/src/core/interpolate/interpolate.spec.js +29 -29
- package/src/core/interval/interval.spec.js +1 -1
- package/src/core/location/location.spec.js +1 -1
- package/src/core/parser/lexer.spec.js +1 -1
- package/src/core/parser/parse.spec.js +1 -1
- package/src/core/q/q.js +1 -0
- package/src/core/q/q.spec.js +1 -1
- package/src/core/scope/scope.js +1 -1
- package/src/core/timeout/timeout.js +5 -21
- package/src/directive/attrs/boolean.spec.js +1 -1
- package/src/directive/bind/bind.js +2 -2
- package/src/directive/class/class.js +1 -1
- package/src/directive/form/form.js +1 -18
- package/src/directive/form/form.spec.js +2 -2
- package/src/directive/include/include.js +7 -0
- package/src/directive/input/input.js +1 -1
- package/src/{exts → directive}/messages/messages.html +4 -1
- package/src/directive/messages/messages.js +346 -0
- package/src/{exts → directive}/messages/messages.spec.js +7 -11
- package/src/{exts → directive}/messages/messages.test.js +1 -1
- package/src/directive/model-options/model-options.spec.js +1 -1
- package/src/directive/switch/switch.spec.js +1 -1
- package/src/exts/aria/aria.spec.js +1 -1
- package/src/filters/filter.js +2 -3
- package/src/filters/filter.spec.js +1 -1
- package/src/filters/filters.spec.js +1 -1
- package/src/filters/limit-to.spec.js +2 -2
- package/src/loader.js +1 -1
- package/src/loader.spec.js +1 -1
- package/src/public.js +12 -2
- package/src/router/common/glob.spec.js +1 -1
- package/src/router/params/param-factory.js +1 -1
- package/src/router/state/state-builder.spec.js +1 -1
- package/src/router/template-factory.js +6 -4
- package/src/router/view/view.spec.js +8 -11
- package/src/router/view-scroll.js +6 -1
- package/src/services/anchor-scroll.html +83 -0
- package/src/services/anchor-scroll.js +23 -6
- package/src/services/browser.js +1 -1
- package/src/services/http/http.spec.js +40 -40
- package/src/shared/jqlite/jqlite.js +3 -3
- package/src/shared/jqlite/jqlite.spec.js +4 -4
- package/src/shared/utils.js +1 -1
- package/types/core/q/q.d.ts +5 -0
- package/types/core/scope/scope.d.ts +1 -1
- package/types/core/timeout/timeout.d.ts +3 -7
- package/types/directive/form/form.d.ts +3 -20
- package/types/directive/include/include.d.ts +1 -1
- package/types/router/params/param-factory.d.ts +1 -1
- package/types/router/template-factory.d.ts +4 -4
- package/types/router/view-scroll.d.ts +1 -1
- package/types/services/anchor-scroll.d.ts +16 -1
- package/types/services/browser.d.ts +2 -2
- package/src/exts/messages/messages.js +0 -361
- package/types/exts/messages/messages.d.ts +0 -1
- /package/src/{exts → directive}/messages/messages.md +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { markQExceptionHandled } from "../q/q";
|
|
2
|
-
import { isDefined,
|
|
2
|
+
import { isDefined, minErr, sliceArgs } from "../../shared/utils";
|
|
3
3
|
|
|
4
4
|
const $timeoutMinErr = minErr("$timeout");
|
|
5
5
|
|
|
@@ -23,22 +23,16 @@ export function $TimeoutProvider() {
|
|
|
23
23
|
const deferreds = {};
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
|
|
27
|
-
* @name $timeout
|
|
28
|
-
*
|
|
29
|
-
* @description
|
|
26
|
+
|
|
30
27
|
* AngularJS's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
|
|
31
28
|
* block and delegates any exceptions to
|
|
32
|
-
* {@link
|
|
29
|
+
* {@link $exceptionHandler} service.
|
|
33
30
|
*
|
|
34
31
|
* The return value of calling `$timeout` is a promise, which will be resolved when
|
|
35
32
|
* the delay has passed and the timeout function, if provided, is executed.
|
|
36
33
|
*
|
|
37
34
|
* To cancel a timeout request, call `$timeout.cancel(promise)`.
|
|
38
35
|
*
|
|
39
|
-
* In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
|
|
40
|
-
* synchronously flush the queue of deferred functions.
|
|
41
|
-
*
|
|
42
36
|
* If you only want a promise that will be resolved after some specified delay
|
|
43
37
|
* then you can call `$timeout` without the `fn` function.
|
|
44
38
|
*
|
|
@@ -46,17 +40,11 @@ export function $TimeoutProvider() {
|
|
|
46
40
|
* @param {number=} [delay=0] Delay in milliseconds.
|
|
47
41
|
* @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
|
|
48
42
|
* will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
|
|
49
|
-
* @returns {
|
|
43
|
+
* @returns {import("../q/q").QPromise<any>} Promise that will be resolved when the timeout is reached. The promise
|
|
50
44
|
* will be resolved with the return value of the `fn` function.
|
|
51
45
|
*
|
|
52
46
|
*/
|
|
53
47
|
function timeout(fn, delay, invokeApply = true) {
|
|
54
|
-
if (!isFunction(fn)) {
|
|
55
|
-
invokeApply = delay;
|
|
56
|
-
delay = fn;
|
|
57
|
-
fn = () => {};
|
|
58
|
-
}
|
|
59
|
-
|
|
60
48
|
const args = sliceArgs(arguments, 3);
|
|
61
49
|
const skipApply = isDefined(invokeApply) && !invokeApply;
|
|
62
50
|
const deferred = (skipApply ? $$q : $q).defer();
|
|
@@ -87,14 +75,10 @@ export function $TimeoutProvider() {
|
|
|
87
75
|
}
|
|
88
76
|
|
|
89
77
|
/**
|
|
90
|
-
* @ngdoc method
|
|
91
|
-
* @name $timeout#cancel
|
|
92
|
-
*
|
|
93
|
-
* @description
|
|
94
78
|
* Cancels a task associated with the `promise`. As a result of this, the promise will be
|
|
95
79
|
* resolved with a rejection.
|
|
96
80
|
*
|
|
97
|
-
* @param {
|
|
81
|
+
* @param {import("../q/q").QPromise<any>} promise Promise returned by the `$timeout` function.
|
|
98
82
|
* @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
|
|
99
83
|
* canceled.
|
|
100
84
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Angular } from "../../loader";
|
|
2
2
|
import { createInjector } from "../../core/di/injector";
|
|
3
|
-
import { dealoc
|
|
3
|
+
import { dealoc } from "../../shared/jqlite/jqlite";
|
|
4
4
|
|
|
5
5
|
describe("boolean attr directives", () => {
|
|
6
6
|
let element, $rootScope, $compile, $rootElement;
|
|
@@ -37,8 +37,8 @@ export const ngBindHtmlDirective = [
|
|
|
37
37
|
return {
|
|
38
38
|
restrict: "A",
|
|
39
39
|
compile: (_tElement, tAttrs) => {
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
const ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
|
|
41
|
+
const ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, (val) => val);
|
|
42
42
|
return (scope, element) => {
|
|
43
43
|
scope.$watch(ngBindHtmlWatch, () => {
|
|
44
44
|
// The watched value is the unwrapped value. To avoid re-escaping, use the direct getter.
|
|
@@ -153,12 +153,6 @@ FormController.prototype = {
|
|
|
153
153
|
},
|
|
154
154
|
|
|
155
155
|
/**
|
|
156
|
-
* @ngdoc method
|
|
157
|
-
* @name form.FormController#$addControl
|
|
158
|
-
* @param {object} control control object, either a {@link form.FormController} or an
|
|
159
|
-
* {@link ngModel.NgModelController}
|
|
160
|
-
*
|
|
161
|
-
* @description
|
|
162
156
|
* Register a control with the form. Input elements using ngModelController do this automatically
|
|
163
157
|
* when they are linked.
|
|
164
158
|
*
|
|
@@ -187,11 +181,6 @@ FormController.prototype = {
|
|
|
187
181
|
},
|
|
188
182
|
|
|
189
183
|
/**
|
|
190
|
-
* @ngdoc method
|
|
191
|
-
* @name form.FormController#$getControls
|
|
192
|
-
* @returns {Array} the controls that are currently part of this form
|
|
193
|
-
*
|
|
194
|
-
* @description
|
|
195
184
|
* This method returns a **shallow copy** of the controls that are currently part of this form.
|
|
196
185
|
* The controls can be instances of {@link form.FormController `FormController`}
|
|
197
186
|
* ({@link ngForm "child-forms"}) and of {@link ngModel.NgModelController `NgModelController`}.
|
|
@@ -222,12 +211,6 @@ FormController.prototype = {
|
|
|
222
211
|
},
|
|
223
212
|
|
|
224
213
|
/**
|
|
225
|
-
* @ngdoc method
|
|
226
|
-
* @name form.FormController#$removeControl
|
|
227
|
-
* @param {object} control control object, either a {@link form.FormController} or an
|
|
228
|
-
* {@link ngModel.NgModelController}
|
|
229
|
-
*
|
|
230
|
-
* @description
|
|
231
214
|
* Deregister a control from the form.
|
|
232
215
|
*
|
|
233
216
|
* Input elements using ngModelController do this automatically when they are destroyed.
|
|
@@ -382,7 +365,7 @@ FormController.prototype = {
|
|
|
382
365
|
* (undefined), or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
|
|
383
366
|
* Skipped is used by AngularJS when validators do not run because of parse errors and when
|
|
384
367
|
* `$asyncValidators` do not run because any of the `$validators` failed.
|
|
385
|
-
* @param {NgModelController | FormController} controller - The controller whose validity state is
|
|
368
|
+
* @param {import("../model/model").NgModelController | FormController} controller - The controller whose validity state is
|
|
386
369
|
* triggering the change.
|
|
387
370
|
*/
|
|
388
371
|
addSetValidityMethod({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Angular } from "../../loader";
|
|
2
2
|
import { createInjector } from "../../core/di/injector";
|
|
3
3
|
import { dealoc, JQLite } from "../../shared/jqlite/jqlite";
|
|
4
|
-
import { FormController } from "
|
|
4
|
+
import { FormController } from "./form";
|
|
5
5
|
|
|
6
6
|
describe("form", () => {
|
|
7
7
|
let doc;
|
|
@@ -679,7 +679,7 @@ describe("form", () => {
|
|
|
679
679
|
$compile(doc)(scope);
|
|
680
680
|
scope.$apply();
|
|
681
681
|
|
|
682
|
-
|
|
682
|
+
const parent = scope.parent,
|
|
683
683
|
child = scope.child;
|
|
684
684
|
|
|
685
685
|
expect(parent).toBeDefined();
|
|
@@ -7,6 +7,13 @@ export const ngIncludeDirective = [
|
|
|
7
7
|
"$templateRequest",
|
|
8
8
|
"$anchorScroll",
|
|
9
9
|
"$animate",
|
|
10
|
+
/**
|
|
11
|
+
*
|
|
12
|
+
* @param {*} $templateRequest
|
|
13
|
+
* @param {import("../../services/anchor-scroll").AnchorScrollFunction} $anchorScroll
|
|
14
|
+
* @param {*} $animate
|
|
15
|
+
* @returns
|
|
16
|
+
*/
|
|
10
17
|
($templateRequest, $anchorScroll, $animate) => ({
|
|
11
18
|
restrict: "ECA",
|
|
12
19
|
priority: 400,
|
|
@@ -203,7 +203,7 @@ export function weekParser(isoWeek, existingDate) {
|
|
|
203
203
|
|
|
204
204
|
function getFirstThursdayOfYear(year) {
|
|
205
205
|
// 0 = index of January
|
|
206
|
-
|
|
206
|
+
const dayOfWeekOnFirst = new Date(year, 0, 1).getDay();
|
|
207
207
|
// 4 = index of Thursday (+1 to account for 1st = 5)
|
|
208
208
|
// 11 = index of *next* Thursday (+1 account for 1st = 12)
|
|
209
209
|
return new Date(
|
|
@@ -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
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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,4 +1,4 @@
|
|
|
1
|
-
import { defaultModelOptions } from "
|
|
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";
|
package/src/filters/filter.js
CHANGED
|
@@ -161,9 +161,8 @@ function deepCompare(
|
|
|
161
161
|
|
|
162
162
|
switch (actualType) {
|
|
163
163
|
case "object":
|
|
164
|
-
var key;
|
|
165
164
|
if (matchAgainstAnyProp) {
|
|
166
|
-
for (key in actual) {
|
|
165
|
+
for (let key in actual) {
|
|
167
166
|
// Under certain, rare, circumstances, key may not be a string and `charAt` will be undefined
|
|
168
167
|
// See: https://github.com/angular/angular.js/issues/15644
|
|
169
168
|
if (
|
|
@@ -179,7 +178,7 @@ function deepCompare(
|
|
|
179
178
|
: deepCompare(actual, expected, comparator, anyPropertyKey, false);
|
|
180
179
|
}
|
|
181
180
|
if (expectedType === "object") {
|
|
182
|
-
for (key in expected) {
|
|
181
|
+
for (let key in expected) {
|
|
183
182
|
const expectedVal = expected[key];
|
|
184
183
|
if (isFunction(expectedVal) || isUndefined(expectedVal)) {
|
|
185
184
|
continue;
|
|
@@ -9,7 +9,7 @@ describe("Filter: filter", () => {
|
|
|
9
9
|
beforeEach(() => {
|
|
10
10
|
window.angular = new Angular();
|
|
11
11
|
window.angular.module("myModule", ["ng"]);
|
|
12
|
-
|
|
12
|
+
const injector = createInjector(["myModule"]);
|
|
13
13
|
filter = injector.get("$filter")("filter");
|
|
14
14
|
});
|
|
15
15
|
|
|
@@ -8,7 +8,7 @@ describe("filters", () => {
|
|
|
8
8
|
beforeEach(() => {
|
|
9
9
|
window.angular = new Angular();
|
|
10
10
|
window.angular.module("myModule", ["ng"]);
|
|
11
|
-
|
|
11
|
+
const injector = createInjector(["myModule"]);
|
|
12
12
|
filter = injector.get("$filter");
|
|
13
13
|
});
|
|
14
14
|
|
|
@@ -12,8 +12,8 @@ describe("Filter: limitTo", () => {
|
|
|
12
12
|
beforeEach(() => {
|
|
13
13
|
window.angular = new Angular();
|
|
14
14
|
window.angular.module("myModule", ["ng"]);
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
const injector = createInjector(["myModule"]);
|
|
16
|
+
const $filter = injector.get("$filter");
|
|
17
17
|
|
|
18
18
|
items = ["a", "b", "c", "d", "e", "f", "g", "h"];
|
|
19
19
|
str = "tuvwxyz";
|
package/src/loader.js
CHANGED