The new Angular2 Animation DSL

Cordova vs. Zone.js or "Why is Angular's document event listener not in a zone?"

If you put an Angular Single Page Application into a Cordova wrapped application any "document:*" host listener (e.g. "document:click") won't be triggered with a zone update. This means Angular is not running the change detection to see if there is any update to be processed in the view etc.

The reason for this is that Cordova is monkey-patching window.addEventListener and document.addEventListener. But wait! Isn't Zone.js also monkey-patching these, too? Yes, indeed. But this is done on EventTarget's prototype directly. Cordova however is assigning a new function to those methods. And here comes the problematic part:

This is breaking the prototype inheritance. Zone.js is not involved anymore! Now, any event listener added won't run in a zone (and trigger the change detection).

But what could you do? Maybe call run of NgZone every time in your callback? This would trigger a useless global change detection run in a non-Cordova environment. Fairly not the best idea. Could we just some kind of restore the previous prototype inheritance way? Yes we can! But we need to break it by ourself first. ;)

(function () {
  'use strict';

  window.addEventListener = function () {
    EventTarget.prototype.addEventListener.apply(this, arguments);
  };

  window.removeEventListener = function () {
    EventTarget.prototype.removeEventListener.apply(this, arguments);
  };

  document.addEventListener = function () {
    EventTarget.prototype.addEventListener.apply(this, arguments);
  };

  document.removeEventListener = function () {
    EventTarget.prototype.removeEventListener.apply(this, arguments);
  };
})();

That's all? Yes! But what is happening here? Just some fancy JavaScript magic. When these methods get called, the method of the EventTarget prototype will be called right in the new implementation. The this context will be switched to window and all provided arguments will be forwarded. And the secret is that this needs to be done before cordova.js is executed. In that case Cordova is monkey-patching our implementation which is redirecting the call to the prototype  (later monkey-patched by Zone.js).

And there is the zone again!

Comments

Feed You can follow this conversation by subscribing to the comment feed for this post.

The comments to this entry are closed.