diff --git a/packages/core/primitives/event-dispatch/README.md b/packages/core/primitives/event-dispatch/README.md new file mode 100644 index 0000000000000..6c5113fc64269 --- /dev/null +++ b/packages/core/primitives/event-dispatch/README.md @@ -0,0 +1,272 @@ +# JSAction + +> [!CAUTION] This document serves as a technical documentation of an internal +> library used within Angular. This is **not** a public API that Angular +> provides, avoid using it directly in Angular applications. + +JSAction is a tiny, low-level event delegation library that decouples the +registration of event listeners from the JavaScript code for the listeners +themselves. This enables capturing user interactions before the application is +bootstrapped or hydrated as well as very fine grained lazy loading of event +handling code. It is typically used as a sub-component of a larger framework, +rather than as a stand-alone library. + +## How it works + +The traditional way of adding an event listener is to obtain a reference to the +node and call `.addEventListener` (or use `.onclick`-like properties). However, +this necessarily requires that the code that handles the event has been loaded. +This can introduce a couple problems: + +1. Server rendered applications will silently ignore user events that happen + before the app hydrates and registers handlers + + ```html + + + + ... + + + + +``` + +### 3. Bind to events with `jsaction` attributes + +Add a `jsaction` attribute for every handler in your application that you want +to register with JSAction. If you're embedding JSAction into a framework, you +would probably update your event handling APIs to automatically render these +attributes for your users. + +```html + +``` + +### 4. Register your application with JSAction + +Finally, once your application is bootstrapped and ready to handle events, +you'll need to create a `Dispatcher` and register it with the `EventContract` +that has been queueing events. + + + +```javascript +import {BaseDispatcher as Dispatcher, registerDispatcher} from '@angular/core/primitives/event-dispatch/src/base_dispatcher'; + +function handleEvent(eventInfoWrapper) { + // eventInfoWrapper contains all the information about the event + const eventType = eventInfoWrapper.getEventType(); + const handlerName = eventInfoWrapper.getAction().name; + const event = eventInfoWrapper.getEvent(); + + // Your application or framework must now decide how to get and call the + // appropriate handler. + myApp.runHandler(eventType, handlerName, event); +} + +function eventReplayer(eventInfoWrappers) { + // The dispatcher uses a separate callback for replaying events to allow + // control over how the events are replayed. Here we simply handle them like + // any other event. + for (const eventInfoWrapper of eventInfoWrappers) { + handleEvent(eventInfoWrapper); + } +} + +// Get the contract that we stashed in the other bundle. +const eventContract = window['__ec']; +const dispatcher = new Dispatcher(handleEvent, {eventReplayer}); + +// This will replay any queued events and call handleEvent for each one of them. +registerDispatcher(eventContract, dispatcher); +``` + +Now the application is set up to handle events through JSAction! What the +application does to handle the dispatched events is up to you. It can be as +simple as calling methods in a map keyed by handler name, or as complicated as a +dynamic lazy loading system to load a handler based on the handler name. + +### 5. [optional] Cleanup event contract + +Optionally, you can clean up and remove the event contract from the app if you +plan to replace all jsaction attributes with native event handlers. There are +some tradeoffs to doing this: + +Pros of cleaning up event contract: + + - Native handlers avoid the [quirks](#known-caveats) of JSAction dispatching + +Pros of keeping event contract: + + - JSAction's event delegation drastically reduces the number of event + listeners registered with the browser. In extreme cases, registering + thousands of listeners in your app can be noticably slow. + - There may be slight behavior differences when your event is dispatched via + JSAction vs native event listeners. Always using JSAction dispatch keeps + things consistent. + + + +```javascript +window['__ec'].cleanUp(); +``` + +## Known caveats + +Because JSAction may potentially replay queued events some time after the events +originally fired, certain APIs like `e.preventDefault()` or +`e.stopPropagation()` won't function correctly. + + \ No newline at end of file