Skip to content
This repository was archived by the owner on Mar 20, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,21 +75,21 @@ replayer.replayAll();
#### PrebootOptions

* `appRoot` (**required**) - One or more selectors for apps in the page (i.e. so one string or an array of strings).
* `buffer` (default true) - If true, preboot will attempt to buffer client rendering to an extra hidden div. In most
* `buffer` (default `true`) - If true, preboot will attempt to buffer client rendering to an extra hidden div. In most
cases you will want to leave the default (i.e. true) but may turn off if you are debugging an issue.
* `minify` (deprecated) - minification has been removed in v6. Minification should be handled by the end-user
* `eventSelectors` (defaults below) - This is an array of objects which specify what events preboot should be listening for
on the server view and how preboot should replay those events to the client view.
See Event Selector section below for more details but note that in most cases, you can just rely on the defaults
and you don't need to explicitly set anything here.
* `noReplay` (default false) - The only reason why you would want to set this to true is if you want to
manually trigger the replay yourself. This contrasts with the event selector `noReplay`, because this option is global
* `replay` (default `true`) - The only reason why you would want to set this to `false` is if you want to
manually trigger the replay yourself. This contrasts with the event selector `replay`, because this option is global

This comes in handy for situations where you want to hold off
on the replay and buffer switch until AFTER some async events occur (i.e. route loading, http calls, etc.). By
default, replay occurs right after bootstrap is complete. In some apps, there are more events after bootstrap
however where the page continues to change in significant ways. Basically if you are making major changes to
the page after bootstrap then you will see some jank unless you set `noReplay` to `true` and then trigger replay
the page after bootstrap then you will see some jank unless you set `replay` to `false` and then trigger replay
yourself once you know that all async events are complete.

To manually trigger replay, simply inject the EventReplayer like this:
Expand Down Expand Up @@ -118,12 +118,12 @@ Each event selector has the following properties:
* `events` - An array of event names to listen for (ex. `['focusin', 'keyup', 'click']`)
* `keyCodes` - Only do something IF event includes a key pressed that matches the given key codes.
Useful for doing something when user hits return in a input box or something similar.
* `preventDefault` - If true, `event.preventDefault()` will be called to prevent any further event propagation.
* `freeze` - If true, the UI will freeze which means displaying a translucent overlay which prevents
* `preventDefault` - If `true`, `event.preventDefault()` will be called to prevent any further event propagation.
* `freeze` - If `true`, the UI will freeze which means displaying a translucent overlay which prevents
any further user action until preboot is complete.
* `action` - This is a function callback for any custom code you want to run when this event occurs
in the server view.
* `noReplay` - If true, the event won't be recorded or replayed. Useful when you utilize one of the other options above.
* `replay` - If `false`, the event won't be recorded or replayed. Useful when you utilize one of the other options above.

Here are some examples of event selectors from the defaults:

Expand All @@ -141,7 +141,7 @@ var eventSelectors = [
{ selector: 'form', events: ['submit'], preventDefault: true, freeze: true },

// for tracking focus (no need to replay)
{ selector: 'input,textarea', events: ['focusin', 'focusout', 'mousedown', 'mouseup'], noReplay: true },
{ selector: 'input,textarea', events: ['focusin', 'focusout', 'mousedown', 'mouseup'], replay: false },

// user clicks on a button
{ selector: 'button', events: ['click'], preventDefault: true, freeze: true }
Expand All @@ -152,12 +152,12 @@ var eventSelectors = [

Preboot registers its reply code at the `APP_BOOTSTRAP_LISTENER` token which is called by Angular for every component that is bootstrapped. If you don't have the `bootstrap` property defined in your `AppModule`'s `NgModule` but you instead use the `ngDoBootrap` method (which is done e.g. when using ngUpgrade) this code will not run at all.

To make Preboot work correctly in such a case you need to specify `noReplay: true` in the Preboot options and replay the events yourself. That is, import `PrebootModule` like this:
To make Preboot work correctly in such a case you need to specify `replay: false` in the Preboot options and replay the events yourself. That is, import `PrebootModule` like this:

```typescript
PrebootModule.withConfig({
appRoot: 'app-root',
noReplay: true,
replay: false,
})
```

Expand Down
11 changes: 9 additions & 2 deletions src/lib/api/event.recorder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export function start(prebootData: PrebootData, win?: PrebootWindow) {

const _document = <Document>(theWindow.document || {});
const opts = prebootData.opts || ({} as PrebootOptions);
const eventSelectors = opts.eventSelectors || [];
let eventSelectors = opts.eventSelectors || [];

// create an overlay that can be used later if a freeze event occurs
prebootData.overlay = createOverlay(_document);
Expand All @@ -97,6 +97,13 @@ export function start(prebootData: PrebootData, win?: PrebootWindow) {
prebootData.apps.push(appData);
}

eventSelectors = eventSelectors.map(eventSelector => {
if (!eventSelector.hasOwnProperty('replay')) {
eventSelector.replay = true;
}
return eventSelector;
});

// loop through all the eventSelectors and create event handlers
eventSelectors.forEach(eventSelector => handleEvents(prebootData, appData, eventSelector));
});
Expand Down Expand Up @@ -287,7 +294,7 @@ export function createListenHandler(

// we will record events for later replay unless explicitly marked as
// doNotReplay
if (!eventSelector.noReplay) {
if (eventSelector.replay) {
appData.events.push({
node,
nodeKey,
Expand Down
5 changes: 3 additions & 2 deletions src/lib/api/inline.preboot.code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {PrebootOptions} from '../common/preboot.interfaces';
import {EventSelector, PrebootOptions} from '../common/preboot.interfaces';
import {getNodeKeyForPreboot} from '../common/get-node-key';

import {
Expand Down Expand Up @@ -36,6 +36,7 @@ const eventRecorder = {
export const defaultOptions = <PrebootOptions>{
buffer: true,
minify: true,
replay: true,

// these are the default events are are listening for an transfering from
// server view to client view
Expand Down Expand Up @@ -68,7 +69,7 @@ export const defaultOptions = <PrebootOptions>{
{
selector: 'input,textarea',
events: ['focusin', 'focusout', 'mousedown', 'mouseup'],
noReplay: true
replay: false
},

// user clicks on a button
Expand Down
4 changes: 2 additions & 2 deletions src/lib/common/preboot.interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface EventSelector {
preventDefault?: boolean; // will prevent default handlers if true
freeze?: boolean; // if true, the UI will freeze when this event occurs
action?: Function; // custom action to take with this event
noReplay?: boolean; // if true, no replay will occur
replay?: boolean; // if false, no replay will occur
}

export interface ServerClientRoot {
Expand All @@ -30,7 +30,7 @@ export interface PrebootOptions {
buffer?: boolean; // if true, attempt to buffer client rendering to hidden div
eventSelectors?: EventSelector[]; // when any of these events occur, they are recorded
appRoot: string | string[]; // define selectors for one or more server roots
noReplay?: boolean;
replay?: boolean;
}

// our wrapper around DOM events in preboot
Expand Down
16 changes: 10 additions & 6 deletions src/lib/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export function prebootHook(doc: Document,
eventReplayer: EventReplayer) {
// necessary because of angular/angular/issues/14485
const res = () => {

if (isPlatformServer(platformId)) {
const inlineCode = getInlinePrebootCode(prebootOpts);
const script = doc.createElement('script');
Expand All @@ -44,14 +45,17 @@ export function prebootHook(doc: Document,
script.textContent = inlineCode;
doc.head.appendChild(script);
}
if (isPlatformBrowser(platformId) && !prebootOpts.noReplay) {
appRef.isStable
.pipe(
filter(stable => stable),
take(1)
).subscribe(() => {
if (isPlatformBrowser(platformId)) {
const replay = prebootOpts.replay != null ? prebootOpts.replay : true;
if (replay) {
appRef.isStable
.pipe(
filter(stable => stable),
take(1)
).subscribe(() => {
eventReplayer.replayAll();
});
}
}
};

Expand Down