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

Commit abce072

Browse files
author
Kent C. Dodds
committed
Merge pull request #422 from acsl/master
Fix for #358 that works around the fact that IE8 does not support querySelectorAll(':not()')
2 parents 889694b + 9663c70 commit abce072

File tree

2 files changed

+77
-8
lines changed

2 files changed

+77
-8
lines changed

src/run/formlyNgModelAttrsManipulator.js

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,40 @@ function addFormlyNgModelAttrsManipulator(formlyConfig, $interpolate, formlyWarn
124124
const selectorNot = angular.isString(skip) ? `:not(${skip})` : '';
125125
const skipNot = ':not([formly-skip-ng-model-attrs-manipulator])';
126126
const query = `[ng-model]${selectorNot}${skipNot}, [data-ng-model]${selectorNot}${skipNot}`;
127-
return node.querySelectorAll(query);
127+
try {
128+
return node.querySelectorAll(query);
129+
} catch (e) {
130+
//this code is needed for IE8, as it does not support the CSS3 ':not' selector
131+
//it should be removed when IE8 support is dropped
132+
return getNgModelNodesFallback(node, skip);
133+
}
134+
}
135+
136+
function getNgModelNodesFallback(node, skip) {
137+
const allNgModelNodes = node.querySelectorAll('[ng-model], [data-ng-model]');
138+
const matchingNgModelNodes = [];
139+
140+
//make sure this array is compatible with NodeList type by adding an 'item' function
141+
matchingNgModelNodes.item = function(i) {
142+
return this[i];
143+
};
144+
145+
for (let i = 0; i < allNgModelNodes.length; i++) {
146+
const ngModelNode = allNgModelNodes[i];
147+
if (!ngModelNode.hasAttribute('formly-skip-ng-model-attrs-manipulator') &&
148+
(angular.isString(skip) || !nodeMatches(ngModelNode, skip))) {
149+
150+
matchingNgModelNodes.push(ngModelNode);
151+
}
152+
}
153+
154+
return matchingNgModelNodes;
155+
}
156+
157+
function nodeMatches(node, selector) {
158+
const div = document.createElement('div');
159+
div.innerHTML = node.outerHTML;
160+
return div.querySelector(selector);
128161
}
129162

130163
function getSkip(options) {

src/run/formlyNgModelAttrsManipulator.test.js

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ describe('formlyNgModelAttrsManipulator', () => {
4545
expect(result).to.equal(template);
4646
});
4747

48-
it(`should allow you to specify a selector for specific elements to skip`, () => {
48+
49+
const skipWithSelectorTitle = `should allow you to specify a selector for specific elements to skip`;
50+
function skipWithSelector() {
4951
const className = 'ignored-thing' + _.random(0, 10);
5052
field.templateOptions.required = true;
5153
field.extras.skipNgModelAttrsManipulator = `.${className}`;
@@ -59,9 +61,11 @@ describe('formlyNgModelAttrsManipulator', () => {
5961
const secondInput = angular.element(resultNode.querySelector(`.${className}`));
6062
expect(firstInput.attr('required')).to.exist;
6163
expect(secondInput.attr('required')).to.not.exist;
62-
});
64+
}
65+
it(skipWithSelectorTitle, skipWithSelector);
6366

64-
it(`should allow you to place the attribute formly-skip-ng-model-attrs-manipulator on an ng-model to have it skip`, () => {
67+
const skipWithAttributeTitle = `should allow you to place the attribute formly-skip-ng-model-attrs-manipulator on an ng-model to have it skip`;
68+
function skipWithAttribute() {
6569
field.templateOptions.required = true;
6670
manipulate(`
6771
<div>
@@ -73,10 +77,12 @@ describe('formlyNgModelAttrsManipulator', () => {
7377
const secondInput = angular.element(resultNode.querySelector('[formly-skip-ng-model-attrs-manipulator]'));
7478
expect(firstInput.attr('required')).to.exist;
7579
expect(secondInput.attr('required')).to.not.exist;
76-
});
80+
}
81+
it(skipWithAttributeTitle, skipWithAttribute);
7782

7883

79-
it(`should not skip by selector if skipNgModelAttrsManipulator is a boolean value`, () => {
84+
const dontSkipWithBooleanTitle = `should not skip by selector if skipNgModelAttrsManipulator is a boolean value`;
85+
function dontSkipWithBoolean() {
8086
field.templateOptions.required = true;
8187
field.extras.skipNgModelAttrsManipulator = false;
8288
manipulate(`
@@ -89,9 +95,11 @@ describe('formlyNgModelAttrsManipulator', () => {
8995
const secondInput = angular.element(resultNode.querySelector('.second-thing'));
9096
expect(firstInput.attr('required')).to.exist;
9197
expect(secondInput.attr('required')).to.exist;
92-
});
98+
}
99+
it(dontSkipWithBooleanTitle, dontSkipWithBoolean);
93100

94-
it(`should allow you to skip using both the special attribute and the custom selector`, () => {
101+
const skipWithAttributeAndSelectorTitle = `should allow you to skip using both the special attribute and the custom selector`;
102+
function skipWithAttributeAndSelector() {
95103
const className = 'ignored-thing' + _.random(0, 10);
96104
field.templateOptions.required = true;
97105
field.extras.skipNgModelAttrsManipulator = `.${className}`;
@@ -108,9 +116,37 @@ describe('formlyNgModelAttrsManipulator', () => {
108116
expect(firstInput.attr('required')).to.exist;
109117
expect(secondInput.attr('required')).to.not.exist;
110118
expect(thirdInput.attr('required')).to.not.exist;
119+
}
120+
it(skipWithAttributeAndSelectorTitle, skipWithAttributeAndSelector);
121+
122+
//repeat a few skipping tests with a broken Element.querySelectorAll function
123+
describe('node search fallback', () => {
124+
let origQuerySelectorAll;
125+
126+
//deliberately break querySelectorAll to mimic IE8's behaviour
127+
beforeEach(() => {
128+
origQuerySelectorAll = Element.prototype.querySelectorAll;
129+
Element.prototype.querySelectorAll = function brokenQuerySelectorAll(selector) {
130+
if (selector && selector.indexOf(':not') >= 0) {
131+
throw new Error(':not selector not supported');
132+
}
133+
return origQuerySelectorAll.apply(this, arguments);
134+
};
135+
});
136+
137+
afterEach(() => {
138+
Element.prototype.querySelectorAll = origQuerySelectorAll;
139+
});
140+
141+
it(skipWithSelectorTitle, skipWithSelector);
142+
it(skipWithAttributeTitle, skipWithAttribute);
143+
it(dontSkipWithBooleanTitle, dontSkipWithBoolean);
144+
it(skipWithAttributeAndSelectorTitle, skipWithAttributeAndSelector);
111145
});
146+
112147
});
113148

149+
114150
it(`should have a limited number of automatically added attributes without any specific options`, () => {
115151
manipulate();
116152
// because different browsers place attributes in different places...

0 commit comments

Comments
 (0)