From ed13fe79a2ff8b021e29b227611653b9e6e79008 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82e=CC=A8biowski?= Date: Sun, 9 Oct 2016 23:51:53 +0200 Subject: [PATCH 1/5] feat(jqLite): implement jqLite(f) as alias to jqLite(document).ready(f) jQuery has supported this form for a long time. As of jQuery 3.0 this form is the preferred one and all others are deprecated so jqLite(f) is now also supported. All internal invocations of jqLite(document).ready(f) (& equivalent) have been replaced by jqLite(f). Tests for these methods have been added as jqLite#ready had no explicit tests so far. --- docs/content/guide/bootstrap.ngdoc | 4 +-- src/angular.bind.js | 7 ++--- src/angular.suffix | 2 +- src/jqLite.js | 45 +++++++++++++++++------------- src/ngScenario/angular.suffix | 2 +- test/e2e/fixtures/ready/index.html | 13 +++++++++ test/e2e/fixtures/ready/script.js | 32 +++++++++++++++++++++ test/e2e/tests/ready.spec.js | 25 +++++++++++++++++ 8 files changed, 103 insertions(+), 27 deletions(-) create mode 100644 test/e2e/fixtures/ready/index.html create mode 100644 test/e2e/fixtures/ready/script.js create mode 100644 test/e2e/tests/ready.spec.js diff --git a/docs/content/guide/bootstrap.ngdoc b/docs/content/guide/bootstrap.ngdoc index f0a896baa1d..15d4ae9a13a 100644 --- a/docs/content/guide/bootstrap.ngdoc +++ b/docs/content/guide/bootstrap.ngdoc @@ -115,7 +115,7 @@ Here is an example of manually initializing Angular: $scope.greetMe = 'World'; }]); - angular.element(document).ready(function() { + angular.element(function() { angular.bootstrap(document, ['myApp']); }); @@ -167,4 +167,4 @@ until `angular.resumeBootstrap()` is called. `angular.resumeBootstrap()` takes an optional array of modules that should be added to the original list of modules that the app was -about to be bootstrapped with. \ No newline at end of file +about to be bootstrapped with. diff --git a/src/angular.bind.js b/src/angular.bind.js index 45bcdecb724..e23e8915a7e 100644 --- a/src/angular.bind.js +++ b/src/angular.bind.js @@ -1,12 +1,11 @@ if (window.angular.bootstrap) { - //AngularJS is already loaded, so we can return here... + // AngularJS is already loaded, so we can return here... if (window.console) { console.log('WARNING: Tried to load angular more than once.'); } return; } -//try to bind to jquery now so that one can write jqLite(document).ready() -//but we will rebind on bootstrap again. +// try to bind to jquery now so that one can write jqLite(fn) +// but we will rebind on bootstrap again. bindJQuery(); - diff --git a/src/angular.suffix b/src/angular.suffix index 9cdd66db8a4..fddb3d072eb 100644 --- a/src/angular.suffix +++ b/src/angular.suffix @@ -1,4 +1,4 @@ - jqLite(window.document).ready(function() { + jqLite(function() { angularInit(window.document, bootstrap); }); diff --git a/src/jqLite.js b/src/jqLite.js index 92da9ef6fa6..39d5b84fac8 100644 --- a/src/jqLite.js +++ b/src/jqLite.js @@ -288,6 +288,8 @@ function JQLite(element) { if (argIsString) { jqLiteAddNodes(this, jqLiteParseHTML(element)); + } else if (isFunction(element)) { + jqLiteReady(element); } else { jqLiteAddNodes(this, element); } @@ -519,29 +521,34 @@ function jqLiteDocumentLoaded(action, win) { } } +function jqLiteReady(fn) { + var fired = false; + + function trigger() { + if (fired) return; + fired = true; + fn(); + } + + // check if document is already loaded + if (window.document.readyState === 'complete') { + window.setTimeout(trigger); + } else { + // We can not use jqLite since we are not done loading and jQuery could be loaded later. + + // Works for modern browsers and IE9 + window.document.addEventListener('DOMContentLoaded', trigger); + + // Fallback to window.onload for others + window.addEventListener('load', trigger); + } +} + ////////////////////////////////////////// // Functions which are declared directly. ////////////////////////////////////////// var JQLitePrototype = JQLite.prototype = { - ready: function(fn) { - var fired = false; - - function trigger() { - if (fired) return; - fired = true; - fn(); - } - - // check if document is already loaded - if (window.document.readyState === 'complete') { - window.setTimeout(trigger); - } else { - this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9 - // we can not use jqLite since we are not done loading and jQuery could be loaded later. - // eslint-disable-next-line new-cap - JQLite(window).on('load', trigger); // fallback to window.onload for others - } - }, + ready: jqLiteReady, toString: function() { var value = []; forEach(this, function(e) { value.push('' + e);}); diff --git a/src/ngScenario/angular.suffix b/src/ngScenario/angular.suffix index bb2bd6af284..5c89e61036f 100644 --- a/src/ngScenario/angular.suffix +++ b/src/ngScenario/angular.suffix @@ -14,7 +14,7 @@ angular.forEach(script.attributes, function(attr) { }); if (config.autotest) { - JQLite(window.document).ready(function() { + JQLite(function() { angular.scenario.setUpAndRun(config); }); } diff --git a/test/e2e/fixtures/ready/index.html b/test/e2e/fixtures/ready/index.html new file mode 100644 index 00000000000..732ce0690dc --- /dev/null +++ b/test/e2e/fixtures/ready/index.html @@ -0,0 +1,13 @@ + + + + {{beforeReady}} + {{afterReady}} + {{afterReadySync}} + {{afterReadyMethod}} + {{afterReadyMethodSync}} + + +
This div is loaded after scripts.
+ + diff --git a/test/e2e/fixtures/ready/script.js b/test/e2e/fixtures/ready/script.js new file mode 100644 index 00000000000..77713e4606c --- /dev/null +++ b/test/e2e/fixtures/ready/script.js @@ -0,0 +1,32 @@ +'use strict'; + +var beforeReady; +(function() { + var divAfterScripts = window.document.getElementById('div-after-scripts'); + beforeReady = divAfterScripts && divAfterScripts.textContent; +})(); + +var afterReady; +angular.element(function() { + var divAfterScripts = window.document.getElementById('div-after-scripts'); + afterReady = divAfterScripts && divAfterScripts.textContent; +}); + +var afterReadyMethod; +angular.element(window.document).ready(function() { + var divAfterScripts = window.document.getElementById('div-after-scripts'); + afterReadyMethod = divAfterScripts && divAfterScripts.textContent; +}); + +var afterReadySync = afterReady; +var afterReadyMethodSync = afterReadyMethod; + +angular + .module('test', []) + .run(function($rootScope) { + $rootScope.beforeReady = beforeReady; + $rootScope.afterReady = afterReady; + $rootScope.afterReadySync = afterReadySync; + $rootScope.afterReadyMethod = afterReadyMethod; + $rootScope.afterReadyMethodSync = afterReadyMethodSync; + }); diff --git a/test/e2e/tests/ready.spec.js b/test/e2e/tests/ready.spec.js new file mode 100644 index 00000000000..d2576b8c673 --- /dev/null +++ b/test/e2e/tests/ready.spec.js @@ -0,0 +1,25 @@ +'use strict'; + +describe('Firing a callback on ready', function() { + it('should not have the div available immediately', function() { + loadFixture('ready'); + expect(element(by.className('before-ready')).getText()) + .toBe(''); + }); + + it('should wait for document ready', function() { + loadFixture('ready'); + expect(element(by.className('after-ready')).getText()) + .toBe('This div is loaded after scripts.'); + expect(element(by.className('after-ready-method')).getText()) + .toBe('This div is loaded after scripts.'); + }); + + it('should be asynchronous', function() { + loadFixture('ready'); + expect(element(by.className('after-ready-sync')).getText()) + .toBe(''); + expect(element(by.className('after-ready-method-sync')).getText()) + .toBe(''); + }); +}); From 272b511a0851929927457c8f5da30705d153055f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82e=CC=A8biowski?= Date: Sun, 9 Oct 2016 23:53:00 +0200 Subject: [PATCH 2/5] refactor(jqLite): deprecate jqLite#ready Use jqLite(fn) instead of jqLite(document).ready(fn). --- src/jqLite.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jqLite.js b/src/jqLite.js index 39d5b84fac8..500bfb11a18 100644 --- a/src/jqLite.js +++ b/src/jqLite.js @@ -76,7 +76,7 @@ * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors * - [`prepend()`](http://api.jquery.com/prepend/) * - [`prop()`](http://api.jquery.com/prop/) - * - [`ready()`](http://api.jquery.com/ready/) + * - [`ready()`](http://api.jquery.com/ready/) (_deprecated_, use `angular.element(callback)` instead of `angular.element(document).ready(callback)`) * - [`remove()`](http://api.jquery.com/remove/) * - [`removeAttr()`](http://api.jquery.com/removeAttr/) - Does not support multiple attributes * - [`removeClass()`](http://api.jquery.com/removeClass/) - Does not support a function as first argument From 7d2c5df5e47ae7e10e7d6e58fe7ae112057ed789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82e=CC=A8biowski?= Date: Wed, 12 Oct 2016 14:38:31 +0200 Subject: [PATCH 3/5] docs(jqLite): remove the removal plan info for bind/unbind We're not going to remove the aliases before jQuery does. --- src/jqLite.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jqLite.js b/src/jqLite.js index 500bfb11a18..2ded6674628 100644 --- a/src/jqLite.js +++ b/src/jqLite.js @@ -56,7 +56,7 @@ * - [`after()`](http://api.jquery.com/after/) * - [`append()`](http://api.jquery.com/append/) * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters - * - [`bind()`](http://api.jquery.com/bind/) (_deprecated_ - to be removed in 1.7.0, use [`on()`](http://api.jquery.com/on/)) - Does not support namespaces, selectors or eventData + * - [`bind()`](http://api.jquery.com/bind/) (_deprecated_, use [`on()`](http://api.jquery.com/on/)) - Does not support namespaces, selectors or eventData * - [`children()`](http://api.jquery.com/children/) - Does not support selectors * - [`clone()`](http://api.jquery.com/clone/) * - [`contents()`](http://api.jquery.com/contents/) @@ -85,7 +85,7 @@ * - [`text()`](http://api.jquery.com/text/) * - [`toggleClass()`](http://api.jquery.com/toggleClass/) - Does not support a function as first argument * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers - * - [`unbind()`](http://api.jquery.com/unbind/) (_deprecated_ - to be removed in 1.7.0, use [`off()`](http://api.jquery.com/off/)) - Does not support namespaces or event object as parameter + * - [`unbind()`](http://api.jquery.com/unbind/) (_deprecated_, use [`off()`](http://api.jquery.com/off/)) - Does not support namespaces or event object as parameter * - [`val()`](http://api.jquery.com/val/) * - [`wrap()`](http://api.jquery.com/wrap/) * From 983bbf7a281f72c2d7b64e49d579d07c7c8f554d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82e=CC=A8biowski?= Date: Wed, 12 Oct 2016 14:41:53 +0200 Subject: [PATCH 4/5] chore(jqLite): remove the ready handlers instead of setting a flag This change aligns jqLite with the jQuery implementation. Closes #15237 --- src/jqLite.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/jqLite.js b/src/jqLite.js index 2ded6674628..94d1b746043 100644 --- a/src/jqLite.js +++ b/src/jqLite.js @@ -522,11 +522,9 @@ function jqLiteDocumentLoaded(action, win) { } function jqLiteReady(fn) { - var fired = false; - function trigger() { - if (fired) return; - fired = true; + window.document.removeEventListener('DOMContentLoaded', trigger); + window.removeEventListener('load', trigger); fn(); } From 8adaec46b0d121f340dc68f6bec59ba8b15cf735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82e=CC=A8biowski?= Date: Wed, 12 Oct 2016 19:10:43 +0200 Subject: [PATCH 5/5] fixup! feat(jqLite): implement jqLite(f) as alias to jqLite(document).ready(f) --- src/jqLite.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jqLite.js b/src/jqLite.js index 94d1b746043..df8565e90ad 100644 --- a/src/jqLite.js +++ b/src/jqLite.js @@ -530,7 +530,7 @@ function jqLiteReady(fn) { // check if document is already loaded if (window.document.readyState === 'complete') { - window.setTimeout(trigger); + window.setTimeout(fn); } else { // We can not use jqLite since we are not done loading and jQuery could be loaded later.