Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 9efb0d5

Browse files
jbedardlgalfaso
authored andcommitted
perf($compile): avoid jquery data calls when there is no data
1 parent 0e622f7 commit 9efb0d5

File tree

4 files changed

+43
-24
lines changed

4 files changed

+43
-24
lines changed

src/jqLite.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,13 @@ function jqLiteAcceptsData(node) {
182182
return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
183183
}
184184

185+
function jqLiteHasData(node) {
186+
for (var key in jqCache[node.ng339]) {
187+
return true;
188+
}
189+
return false;
190+
}
191+
185192
function jqLiteBuildFragment(html, context) {
186193
var tmp, tag, wrap,
187194
fragment = context.createDocumentFragment(),
@@ -556,7 +563,8 @@ function getAliasedAttrName(element, name) {
556563

557564
forEach({
558565
data: jqLiteData,
559-
removeData: jqLiteRemoveData
566+
removeData: jqLiteRemoveData,
567+
hasData: jqLiteHasData
560568
}, function(fn, name) {
561569
JQLite[name] = fn;
562570
});

src/ng/compile.js

+22-20
Original file line numberDiff line numberDiff line change
@@ -2498,26 +2498,28 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
24982498
var fragment = document.createDocumentFragment();
24992499
fragment.appendChild(firstElementToRemove);
25002500

2501-
// Copy over user data (that includes Angular's $scope etc.). Don't copy private
2502-
// data here because there's no public interface in jQuery to do that and copying over
2503-
// event listeners (which is the main use of private data) wouldn't work anyway.
2504-
jqLite(newNode).data(jqLite(firstElementToRemove).data());
2505-
2506-
// Remove data of the replaced element. We cannot just call .remove()
2507-
// on the element it since that would deallocate scope that is needed
2508-
// for the new node. Instead, remove the data "manually".
2509-
if (!jQuery) {
2510-
delete jqLite.cache[firstElementToRemove[jqLite.expando]];
2511-
} else {
2512-
// jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
2513-
// the replaced element. The cleanData version monkey-patched by Angular would cause
2514-
// the scope to be trashed and we do need the very same scope to work with the new
2515-
// element. However, we cannot just cache the non-patched version and use it here as
2516-
// that would break if another library patches the method after Angular does (one
2517-
// example is jQuery UI). Instead, set a flag indicating scope destroying should be
2518-
// skipped this one time.
2519-
skipDestroyOnNextJQueryCleanData = true;
2520-
jQuery.cleanData([firstElementToRemove]);
2501+
if (jqLite.hasData(firstElementToRemove)) {
2502+
// Copy over user data (that includes Angular's $scope etc.). Don't copy private
2503+
// data here because there's no public interface in jQuery to do that and copying over
2504+
// event listeners (which is the main use of private data) wouldn't work anyway.
2505+
jqLite(newNode).data(jqLite(firstElementToRemove).data());
2506+
2507+
// Remove data of the replaced element. We cannot just call .remove()
2508+
// on the element it since that would deallocate scope that is needed
2509+
// for the new node. Instead, remove the data "manually".
2510+
if (!jQuery) {
2511+
delete jqLite.cache[firstElementToRemove[jqLite.expando]];
2512+
} else {
2513+
// jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
2514+
// the replaced element. The cleanData version monkey-patched by Angular would cause
2515+
// the scope to be trashed and we do need the very same scope to work with the new
2516+
// element. However, we cannot just cache the non-patched version and use it here as
2517+
// that would break if another library patches the method after Angular does (one
2518+
// example is jQuery UI). Instead, set a flag indicating scope destroying should be
2519+
// skipped this one time.
2520+
skipDestroyOnNextJQueryCleanData = true;
2521+
jQuery.cleanData([firstElementToRemove]);
2522+
}
25212523
}
25222524

25232525
for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {

test/jqLiteSpec.js

+6
Original file line numberDiff line numberDiff line change
@@ -405,10 +405,13 @@ describe('jqLite', function() {
405405
it('should provide the non-wrapped data calls', function() {
406406
var node = document.createElement('div');
407407

408+
expect(jqLite.hasData(node)).toBe(false);
408409
expect(jqLite.data(node, "foo")).toBeUndefined();
410+
expect(jqLite.hasData(node)).toBe(false);
409411

410412
jqLite.data(node, "foo", "bar");
411413

414+
expect(jqLite.hasData(node)).toBe(true);
412415
expect(jqLite.data(node, "foo")).toBe("bar");
413416
expect(jqLite(node).data("foo")).toBe("bar");
414417

@@ -421,6 +424,9 @@ describe('jqLite', function() {
421424
jqLite.removeData(node);
422425
jqLite.removeData(node);
423426
expect(jqLite.data(node, "bar")).toBeUndefined();
427+
428+
jqLite(node).remove();
429+
expect(jqLite.hasData(node)).toBe(false);
424430
});
425431

426432
it('should emit $destroy event if element removed via remove()', function() {

test/ng/compileSpec.js

+6-3
Original file line numberDiff line numberDiff line change
@@ -5592,8 +5592,13 @@ describe('$compile', function() {
55925592
expect(privateData.events.click).toBeDefined();
55935593
expect(privateData.events.click[0]).toBeDefined();
55945594

5595+
//Ensure the angular $destroy event is still sent
5596+
var destroyCount = 0;
5597+
element.find("div").on("$destroy", function() { destroyCount++; });
5598+
55955599
$rootScope.$apply('xs = null');
55965600

5601+
expect(destroyCount).toBe(2);
55975602
expect(firstRepeatedElem.data('$scope')).not.toBeDefined();
55985603
privateData = jQuery._data(firstRepeatedElem[0]);
55995604
expect(privateData && privateData.events).not.toBeDefined();
@@ -5614,9 +5619,7 @@ describe('$compile', function() {
56145619

56155620
testCleanup();
56165621

5617-
// The initial ng-repeat div is dumped after parsing hence we expect cleanData
5618-
// count to be one larger than size of the iterated array.
5619-
expect(cleanedCount).toBe(xs.length + 1);
5622+
expect(cleanedCount).toBe(xs.length);
56205623

56215624
// Restore the previous jQuery.cleanData.
56225625
jQuery.cleanData = currentCleanData;

0 commit comments

Comments
 (0)