mirror of
https://github.com/LukeHagar/unicorn-utterances.git
synced 2025-12-09 12:57:45 +00:00
chore: add mention of patched event APIs
This commit is contained in:
@@ -794,3 +794,100 @@ export class AppComponent {
|
|||||||
- This, in turn, [calls `refreshView` on the component](https://github.com/angular/angular/blob/a6849f27af129588091f635c6ae7a326241344fc/packages/core/src/render3/instructions/shared.ts#L354)
|
- This, in turn, [calls `refreshView` on the component](https://github.com/angular/angular/blob/a6849f27af129588091f635c6ae7a326241344fc/packages/core/src/render3/instructions/shared.ts#L354)
|
||||||
- [`refreshView` is a wrapper around calling the component's `template` function with `RenderFlags.Update`](https://github.com/angular/angular/blob/a6849f27af129588091f635c6ae7a326241344fc/packages/core/src/render3/instructions/shared.ts#L368)
|
- [`refreshView` is a wrapper around calling the component's `template` function with `RenderFlags.Update`](https://github.com/angular/angular/blob/a6849f27af129588091f635c6ae7a326241344fc/packages/core/src/render3/instructions/shared.ts#L368)
|
||||||
- Which provides the correct flag for the template function, built by the NGC compiler, to update the DOM's contents.
|
- Which provides the correct flag for the template function, built by the NGC compiler, to update the DOM's contents.
|
||||||
|
|
||||||
|
## Zone.js patches more APIs than you might think
|
||||||
|
|
||||||
|
> Hang on a moment... If Zone.js is what triggers `ApplicationRef.tick`, why does it seem to run change detection even when there is seemingly no asynchronous API involved?
|
||||||
|
|
||||||
|
That's a good point! After all, the following Angular component doesn't use `setTimeout` and yet it still triggers `ApplicationRef.tick` (so long as you have NgZone enabled):
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { ApplicationRef, Component, NgZone } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-app',
|
||||||
|
template: `
|
||||||
|
<h1>Hello {{name}}</h1>
|
||||||
|
<button (click)="changeName()">Change Name</button>
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
export class AppComponent {
|
||||||
|
name = '';
|
||||||
|
changeName() {
|
||||||
|
this.name = 'Angular';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Why is that?
|
||||||
|
|
||||||
|
Well, the answer lies within the word "asynchronous" and its literal definition. See, it doesn't just mean "a timer", it means any kind of operation that's non-blocking. That's not just output; that includes user input.
|
||||||
|
|
||||||
|
You might have an inkling that this means that Zone.js patches the `(click)` listener, and you'd be right!
|
||||||
|
|
||||||
|
If we think about how an HTML element is created in JavaScript without a framework, it might look something like this:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const el = document.createElement('button');
|
||||||
|
el.addEventListener('click', () => console.log("I have been pressed"));
|
||||||
|
```
|
||||||
|
|
||||||
|
Well, under-the-hood, the NGC compiler does this same thing with our `(click)` template syntax!
|
||||||
|
|
||||||
|
Knowing that the component's `template` function utilizes `addEventListener`, let's see how Zone.js patches this API.
|
||||||
|
|
||||||
|
The first step to understanding how Zone.js patches `addEventListener`, it's important to know where the browser implements that specific function. You might assume it's a method on [the browser's `HTMLElement` built-in type](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement), but it actually exists on [the browser's built-in `EventTarget` type](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget) instead. `HTMLElement` just inherits this function from `EventTarget`.
|
||||||
|
|
||||||
|
This is why, [in Zone.js' source code, it patches `EventTarget` directly](https://github.com/angular/angular/blob/a6849f27af129588091f635c6ae7a326241344fc/packages/zone.js/lib/browser/browser.ts#L63-L65):
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Zone.js source code
|
||||||
|
// angular/packages/zone.js/lib/browser/browser.ts
|
||||||
|
|
||||||
|
Zone.__load_patch('EventTarget', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
|
||||||
|
patchEvent(global, api);
|
||||||
|
eventTargetPatch(global, api);
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
If we look into `eventTargetPatch`, [we can even see where it patches `ADD_EVENT_LISTENER` specifically](https://github.com/angular/angular/blob/a6849f27af129588091f635c6ae7a326241344fc/packages/zone.js/lib/common/events.ts#L93-L94):
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export function patchEventTarget(
|
||||||
|
_global: any, api: _ZonePrivate, apis: any[], patchOptions?: PatchEventTargetOptions) {
|
||||||
|
const ADD_EVENT_LISTENER = (patchOptions && patchOptions.add) || ADD_EVENT_LISTENER_STR;
|
||||||
|
const REMOVE_EVENT_LISTENER = (patchOptions && patchOptions.rm) || REMOVE_EVENT_LISTENER_STR;
|
||||||
|
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This all means that when the user presses the button in the following example:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { ApplicationRef, Component, NgZone } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-app',
|
||||||
|
template: `
|
||||||
|
<h1>Hello {{name}}</h1>
|
||||||
|
<button (click)="changeName()">Change Name</button>
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
export class AppComponent {
|
||||||
|
name = '';
|
||||||
|
changeName() {
|
||||||
|
this.name = 'Angular';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
It will:
|
||||||
|
|
||||||
|
- Run the `click` `changeName` function
|
||||||
|
- Run `Zone.js`'s patched `addEventListener` function
|
||||||
|
- Trigger the `onMicrotaskEmpty` subscription
|
||||||
|
- `tick` the `ApplicationRef`
|
||||||
|
|
||||||
|
> This is why even in our `runOutsideOfAngular` example, pressing the button more than once will show the live data. The event is being bound and, as a result, the component is being re-rendered with `App.tick` once the bound event is triggered.
|
||||||
|
|||||||
Reference in New Issue
Block a user