mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-01-12 22:45:52 +08:00
Currently Angular monkey-patches a few jQuery methods that remove elements
from the DOM. Since methods like .remove() have multiple signatures
that can change what's actually removed, Angular needs to carefully
repeat them in its patching or it can break apps using jQuery correctly.
Such a strategy is also not future-safe.
Instead of patching individual methods on the prototype, it's better to
hook into jQuery.cleanData and trigger custom events there. This should be
safe as e.g. jQuery UI needs it and uses it. It'll also be future-safe.
The only drawback is that $destroy is no longer triggered when using $detach
but:
1. Angular doesn't use this method, jqLite doesn't implement it.
2. Detached elements can be re-attached keeping all their events & data
so it makes sense that $destroy is not triggered on them.
3. The approach from this commit is so much safer that any issues with
.detach() working differently are outweighed by the robustness of the code.
BREAKING CHANGE: the $destroy event is no longer triggered when using the
jQuery detach() method. If you want to destroy Angular data attached to the
element, use remove().
95 lines
2.5 KiB
JavaScript
95 lines
2.5 KiB
JavaScript
/* global $: false */
|
|
'use strict';
|
|
|
|
if (window.jQuery) {
|
|
|
|
describe('jQuery patch', function() {
|
|
|
|
var doc = null;
|
|
var divSpy = null;
|
|
var spy1 = null;
|
|
var spy2 = null;
|
|
|
|
beforeEach(function() {
|
|
divSpy = jasmine.createSpy('div.$destroy');
|
|
spy1 = jasmine.createSpy('span1.$destroy');
|
|
spy2 = jasmine.createSpy('span2.$destroy');
|
|
doc = $('<div><span class=first>abc</span><span class=second>xyz</span></div>');
|
|
doc.find('span.first').on('$destroy', spy1);
|
|
doc.find('span.second').on('$destroy', spy2);
|
|
});
|
|
|
|
afterEach(function() {
|
|
expect(divSpy).not.toHaveBeenCalled();
|
|
|
|
expect(spy1).toHaveBeenCalled();
|
|
expect(spy1.callCount).toEqual(1);
|
|
expect(spy2).toHaveBeenCalled();
|
|
expect(spy2.callCount).toEqual(1);
|
|
});
|
|
|
|
describe('$detach event', function() {
|
|
|
|
it('should fire on remove()', function() {
|
|
doc.find('span').remove();
|
|
});
|
|
|
|
it('should fire on replaceWith()', function() {
|
|
doc.find('span').replaceWith('<b>bla</b>');
|
|
});
|
|
|
|
it('should fire on replaceAll()', function() {
|
|
$('<b>bla</b>').replaceAll(doc.find('span'));
|
|
});
|
|
|
|
it('should fire on empty()', function() {
|
|
doc.empty();
|
|
});
|
|
|
|
it('should fire on html(param)', function() {
|
|
doc.html('abc');
|
|
});
|
|
|
|
it('should fire on html(\'\')', function() {
|
|
doc.html('');
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('jQuery patch eagerness', function() {
|
|
|
|
var doc = null;
|
|
var divSpy = null;
|
|
var spy1 = null;
|
|
var spy2 = null;
|
|
|
|
beforeEach(function() {
|
|
divSpy = jasmine.createSpy('div.$destroy');
|
|
spy1 = jasmine.createSpy('span1.$destroy');
|
|
spy2 = jasmine.createSpy('span2.$destroy');
|
|
doc = $('<div><span class=first>abc</span><span class=second>xyz</span></div>');
|
|
doc.find('span.first').on('$destroy', spy1);
|
|
doc.find('span.second').on('$destroy', spy2);
|
|
});
|
|
|
|
afterEach(function() {
|
|
expect(divSpy).not.toHaveBeenCalled();
|
|
expect(spy1).not.toHaveBeenCalled();
|
|
});
|
|
|
|
describe('$detach event is not invoked in too many cases', function() {
|
|
|
|
it('should fire only on matched elements on remove(selector)', function() {
|
|
doc.find('span').remove('.second');
|
|
expect(spy2).toHaveBeenCalled();
|
|
expect(spy2.callCount).toEqual(1);
|
|
});
|
|
|
|
it('should not fire on html()', function() {
|
|
doc.html();
|
|
expect(spy2).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
});
|
|
}
|