forked from angular/angular.js
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathngInclude.js
239 lines (221 loc) · 8.15 KB
/
ngInclude.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
'use strict';
/**
* @ngdoc directive
* @name ng.directive:ngInclude
* @restrict ECA
*
* @description
* Fetches, compiles and includes an external HTML fragment.
*
* By default, the template URL is restricted to the same domain and protocol as the
* application document. This is done by calling {@link ng.$sce#methods_getTrustedResourceUrl
* $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
* you may either {@link ng.$sceDelegateProvider#methods_resourceUrlWhitelist whitelist them} or
* {@link ng.$sce#methods_trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
* ng.$sce Strict Contextual Escaping}.
*
* In addition, the browser's
* {@link https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest
* Same Origin Policy} and {@link http://www.w3.org/TR/cors/ Cross-Origin Resource Sharing
* (CORS)} policy may further restrict whether the template is successfully loaded.
* For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
* access on some browsers.
*
* @animations
* enter - animation is used to bring new content into the browser.
* leave - animation is used to animate existing content away.
*
* The enter and leave animation occur concurrently.
*
* @scope
* @priority 400
*
* @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
* make sure you wrap it in quotes, e.g. `src="'myPartialTemplate.html'"`.
* @param {string=} onload Expression to evaluate when a new partial is loaded.
*
* @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
* $anchorScroll} to scroll the viewport after the content is loaded.
*
* - If the attribute is not set, disable scrolling.
* - If the attribute is set without value, enable scrolling.
* - Otherwise enable scrolling only if the expression evaluates to truthy value.
*
* @example
<example animations="true">
<file name="index.html">
<div ng-controller="Ctrl">
<select ng-model="template" ng-options="t.name for t in templates">
<option value="">(blank)</option>
</select>
url of the template: <tt>{{template.url}}</tt>
<hr/>
<div class="slide-animate-container">
<div class="slide-animate" ng-include="template.url"></div>
</div>
</div>
</file>
<file name="script.js">
function Ctrl($scope) {
$scope.templates =
[ { name: 'template1.html', url: 'template1.html'}
, { name: 'template2.html', url: 'template2.html'} ];
$scope.template = $scope.templates[0];
}
</file>
<file name="template1.html">
Content of template1.html
</file>
<file name="template2.html">
Content of template2.html
</file>
<file name="animations.css">
.slide-animate-container {
position:relative;
background:white;
border:1px solid black;
height:40px;
overflow:hidden;
}
.slide-animate {
padding:10px;
}
.slide-animate.ng-enter, .slide-animate.ng-leave {
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
position:absolute;
top:0;
left:0;
right:0;
bottom:0;
display:block;
padding:10px;
}
.slide-animate.ng-enter {
top:-50px;
}
.slide-animate.ng-enter.ng-enter-active {
top:0;
}
.slide-animate.ng-leave {
top:0;
}
.slide-animate.ng-leave.ng-leave-active {
top:50px;
}
</file>
<file name="scenario.js">
it('should load template1.html', function() {
expect(element('.doc-example-live [ng-include]').text()).
toMatch(/Content of template1.html/);
});
it('should load template2.html', function() {
select('template').option('1');
expect(element('.doc-example-live [ng-include]').text()).
toMatch(/Content of template2.html/);
});
it('should change to blank', function() {
select('template').option('');
expect(element('.doc-example-live [ng-include]')).toBe(undefined);
});
</file>
</example>
*/
/**
* @ngdoc event
* @name ng.directive:ngInclude#$includeContentRequested
* @eventOf ng.directive:ngInclude
* @eventType emit on the scope ngInclude was declared in
* @description
* Emitted every time the ngInclude content is requested.
*/
/**
* @ngdoc event
* @name ng.directive:ngInclude#$includeContentLoaded
* @eventOf ng.directive:ngInclude
* @eventType emit on the current ngInclude scope
* @description
* Emitted every time the ngInclude content is reloaded.
*/
var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$animate', '$sce',
function($http, $templateCache, $anchorScroll, $animate, $sce) {
return {
restrict: 'ECA',
priority: 400,
terminal: true,
transclude: 'element',
controller: angular.noop,
compile: function(element, attr) {
var srcExp = attr.ngInclude || attr.src,
onloadExp = attr.onload || '',
autoScrollExp = attr.autoscroll;
return function(scope, $element, $attr, ctrl, $transclude) {
var changeCounter = 0,
currentScope,
currentElement;
var cleanupLastIncludeContent = function() {
if (currentScope) {
currentScope.$destroy();
currentScope = null;
}
if(currentElement) {
$animate.leave(currentElement);
currentElement = null;
}
};
scope.$watch($sce.parseAsResourceUrl(srcExp), function ngIncludeWatchAction(src) {
var afterAnimation = function() {
if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
$anchorScroll();
}
};
var thisChangeId = ++changeCounter;
if (src) {
$http.get(src, {cache: $templateCache}).success(function(response) {
if (thisChangeId !== changeCounter) return;
var newScope = scope.$new();
ctrl.template = response;
// Note: This will also link all children of ng-include that were contained in the original
// html. If that content contains controllers, ... they could pollute/change the scope.
// However, using ng-include on an element with additional content does not make sense...
// Note: We can't remove them in the cloneAttchFn of $transclude as that
// function is called before linking the content, which would apply child
// directives to non existing elements.
var clone = $transclude(newScope, function(clone) {
cleanupLastIncludeContent();
$animate.enter(clone, null, $element, afterAnimation);
});
currentScope = newScope;
currentElement = clone;
currentScope.$emit('$includeContentLoaded');
scope.$eval(onloadExp);
}).error(function() {
if (thisChangeId === changeCounter) cleanupLastIncludeContent();
});
scope.$emit('$includeContentRequested');
} else {
cleanupLastIncludeContent();
ctrl.template = null;
}
});
};
}
};
}];
// This directive is called during the $transclude call of the first `ngInclude` directive.
// It will replace and compile the content of the element with the loaded template.
// We need this directive so that the element content is already filled when
// the link function of another directive on the same element as ngInclude
// is called.
var ngIncludeFillContentDirective = ['$compile',
function($compile) {
return {
restrict: 'ECA',
priority: -400,
require: 'ngInclude',
link: function(scope, $element, $attr, ctrl) {
$element.html(ctrl.template);
$compile($element.contents())(scope);
}
};
}];