Skip to content

Commit 36d5c73

Browse files
committed
Add deferred interpolation demo.
1 parent 7f5d508 commit 36d5c73

File tree

2 files changed

+302
-0
lines changed

2 files changed

+302
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ with.
1010

1111
## My JavaScript Demos - I Love JavaScript!
1212

13+
* [Deferring Attribute Interpolation In AngularJS For Better Performance](http://bennadel.github.io/JavaScript-Demos/demos/deferring-attribute-interpolation-angularjs/)
1314
* [You Cannot Link Attribute Interpolation Multiple Times In AngularJS](http://bennadel.github.io/JavaScript-Demos/demos/cannot-link-attribute-interpolation-multiple-times-angularjs/)
1415
* [Creating An Optimized Switch Directive For Use With ngRepeat In AngularJS](http://bennadel.github.io/JavaScript-Demos/demos/ng-repeat-switch-angularjs/)
1516
* [Mixing Data And Templates Using A Single ng-repeat In AngularJS](http://bennadel.github.io/JavaScript-Demos/demos/mixed-templates-ng-repat-angularjs/)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
<!doctype html>
2+
<html ng-app="Demo">
3+
<head>
4+
<meta charset="utf-8" />
5+
6+
<title>
7+
Deferring Attribute Interpolation In AngularJS For Better Performance
8+
</title>
9+
10+
<style type="text/css">
11+
12+
ul {
13+
list-style-type: none ;
14+
margin: 0px 0px 0px 0px ;
15+
padding: 0px 0px 0px 0px ;
16+
}
17+
18+
li {
19+
background-color: #FAFAFA ;
20+
border: 1px solid #CCCCCC ;
21+
line-height: 20px ;
22+
margin: 0px 0px 3px 0px ;
23+
padding: 5px 5px 5px 5px ;
24+
}
25+
26+
li:hover {
27+
background-color: #FFF0F0 ;
28+
}
29+
30+
</style>
31+
</head>
32+
<body ng-controller="AppController">
33+
34+
<h1>
35+
Deferring Attribute Interpolation In AngularJS For Better Performance
36+
</h1>
37+
38+
<ul>
39+
<!--
40+
Notice that we are using interpolation in two different places in the
41+
following ngRepeat directive: TITLE attribute and HREF attribute.
42+
-->
43+
<li
44+
ng-repeat="friend in friends track by friend.id"
45+
bn-title="My friend, {{ friend.nickname }}">
46+
47+
<a bn-href="#/friend-{{ friend.id }}">{{ friend.id }} - {{ friend.name }}</a>
48+
49+
</li>
50+
</ul>
51+
52+
53+
<!-- Load scripts. -->
54+
<script type="text/javascript" src="../../vendor/jquery/jquery-2.1.0.min.js"></script>
55+
<script type="text/javascript" src="../../vendor/angularjs/angular-1.2.22.min.js"></script>
56+
<script type="text/javascript">
57+
58+
// Create an application module for our demo.
59+
var app = angular.module( "Demo", [] );
60+
61+
62+
// -------------------------------------------------- //
63+
// -------------------------------------------------- //
64+
65+
66+
// I control the root of the application.
67+
app.controller(
68+
"AppController",
69+
function( $scope ) {
70+
71+
// I contain the list of friends being rendered by the ngRepeat.
72+
$scope.friends = buildFriends( 100 );
73+
74+
75+
// ---
76+
// PRIVATE METHODS.
77+
// ---
78+
79+
80+
// I return a new friend object.
81+
function buildFriend( index ) {
82+
83+
var localIndex = ( index % 3 );
84+
85+
if ( localIndex === 0 ) {
86+
87+
return({
88+
id: index,
89+
name: "Sarah",
90+
nickname: "Stubs"
91+
});
92+
93+
} else if ( localIndex === 1 ) {
94+
95+
return({
96+
id: index,
97+
name: "Joanna",
98+
nickname: "J-Diesel"
99+
});
100+
101+
} else {
102+
103+
return({
104+
id: index,
105+
name: "Tricia",
106+
nickname: "Boss"
107+
});
108+
109+
}
110+
111+
}
112+
113+
114+
// I create a collection of friends with the given size.
115+
function buildFriends( size ) {
116+
117+
var friends = [];
118+
119+
for ( var i = 1 ; i <= size ; i++ ) {
120+
121+
friends.push( buildFriend( i ) );
122+
123+
}
124+
125+
return( friends );
126+
127+
}
128+
129+
}
130+
);
131+
132+
133+
// -------------------------------------------------- //
134+
// -------------------------------------------------- //
135+
136+
137+
// I provide an interpolated Title attribute that uses just-in-time interpolation
138+
// rather than a watcher that has to be checked during each digest.
139+
app.directive(
140+
"bnTitle",
141+
function( $interpolate ) {
142+
143+
// When we compile the directive, we have to remove the pre-interpolation
144+
// value so that the watcher will not be created during the pre-linking
145+
// phase of the native AngularJS attribute-interpolation directive.
146+
function compile( tElement, tAttributes ) {
147+
148+
// Compute the interpolation function for the non-interpolated
149+
// attribute value.
150+
var interpolation = $interpolate( tAttributes.bnTitle );
151+
152+
// Clear the value so the attribute-interpolation directive won't
153+
// bind a watcher.
154+
// --
155+
// NOTE: At this point, the interpolation method has already been
156+
// computed twice - once by AngularJS and once by us (above). It will
157+
// actually be computed one more time during the pre-linking phase of
158+
// the attribute-interpolation directive. However, at that point, the
159+
// value (which we are clearing below), will not contain any
160+
// interpolation markers ({{}}), and therefore will not cause any
161+
// watchers to be bound. At that point, further interpolation is our
162+
// responsibility.
163+
tAttributes.$set( "bnTitle", null );
164+
165+
// CAUTION: You CANNOT use this notation (ie, using jQuery to change
166+
// the attribute) at this point - it will not stop the interpolation
167+
// binding from taking place. The reason being (as best I can tell)
168+
// is that the AngularJS "Attributes" collection has already been
169+
// determined and is subsequently used to access the attribute value
170+
// in the pre-linking phased of the interpolation directive.
171+
// --
172+
// tElement.attr( "bnTitle", "" );
173+
174+
175+
// I link the JavaScript events to the current scope.
176+
function link( scope, element, attributes ) {
177+
178+
// Since the "title" is a visual element, we don't need it to
179+
// exist until the user begins to interact with the current
180+
// element. As such, we can defer interpolation until the user
181+
// actually mouses into the current element, at which point the
182+
// native behavior of the browser is to show the title.
183+
element.on(
184+
"mouseenter",
185+
function handleMouseEnterEvent( event ) {
186+
187+
// Get the just-in-time value of the interpolation
188+
// function in the context of the current scope.
189+
// --
190+
// NOTE: We do NOT have to trigger a DIGEST since no
191+
// change to the view-model has taken place. These
192+
// changes are isolated to the DOM. AngularJS doesn't
193+
// need to know about them.
194+
element.attr( "title", interpolation( scope ) );
195+
196+
}
197+
);
198+
199+
}
200+
201+
202+
// Return the linking function.
203+
return( link );
204+
205+
}
206+
207+
// Return the directive configuration.
208+
return({
209+
compile: compile,
210+
restrict: "A"
211+
});
212+
213+
}
214+
);
215+
216+
217+
// -------------------------------------------------- //
218+
// -------------------------------------------------- //
219+
220+
221+
// I provide an interpolated Href attribute that uses just-in-time interpolation
222+
// rather than a watcher that has to be checked during each digest. The HREF is
223+
// calculated on "focus" of the link element.
224+
app.directive(
225+
"bnHref",
226+
function( $interpolate ) {
227+
228+
// When we compile the directive, we have to remove the pre-interpolation
229+
// value so that the watcher will not be created during the pre-linking
230+
// phase of the native AngularJS attribute-interpolation directive.
231+
function compile( tElement, tAttributes ) {
232+
233+
// Compute the interpolation function.
234+
var interpolation = $interpolate( tAttributes.bnHref );
235+
236+
// Clear the attribute since we no longer need it once we have the
237+
// computed interpolation function.
238+
tAttributes.$set( "bnHref", null );
239+
240+
// Set up an empty href so that the link CSS styles will take place.
241+
tElement.attr( "href", "" );
242+
243+
244+
// I link the JavaScript events to the current scope.
245+
function link( scope, element, attributes ) {
246+
247+
// Since the link behavior is activated based on element
248+
// interaction, we can defer interpolation until the user
249+
// focuses the element (either with mouse or keyboard).
250+
element.on(
251+
"focus",
252+
function handleFocusEvent( event ) {
253+
254+
element.attr( "href", interpolation( scope ) );
255+
256+
}
257+
);
258+
259+
}
260+
261+
262+
// Return the linking function.
263+
return( link );
264+
265+
}
266+
267+
// Return the directive configuration.
268+
return({
269+
compile: compile,
270+
restrict: "A"
271+
});
272+
273+
}
274+
);
275+
276+
</script>
277+
278+
</body>
279+
</html>
280+
281+
282+
283+
284+
285+
286+
287+
288+
289+
290+
291+
292+
293+
294+
295+
296+
297+
298+
299+
300+
301+

0 commit comments

Comments
 (0)