Skip to content

Commit

Permalink
Blazor event registry + Updated API
Browse files Browse the repository at this point in the history
  • Loading branch information
MackinnonBuck committed Sep 5, 2023
1 parent 1af8316 commit b7bdce4
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 56 deletions.
2 changes: 1 addition & 1 deletion src/Components/Web.JS/dist/Release/blazor.server.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/Components/Web.JS/dist/Release/blazor.web.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/Components/Web.JS/dist/Release/blazor.webview.js

Large diffs are not rendered by default.

25 changes: 20 additions & 5 deletions src/Components/Web.JS/src/Boot.Web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { NavigationEnhancementCallbacks, attachProgressivelyEnhancedNavigationLi
import { WebRootComponentManager } from './Services/WebRootComponentManager';
import { hasProgrammaticEnhancedNavigationHandler, performProgrammaticEnhancedNavigation } from './Services/NavigationUtils';
import { attachComponentDescriptorHandler, registerAllComponentDescriptors } from './Rendering/DomMerging/DomSync';
import { CallbackCollection } from './Services/CallbackCollection';

let started = false;

Expand All @@ -44,14 +43,12 @@ function boot(options?: Partial<WebStartOptions>) : Promise<void> {
setWebAssemblyOptions(options?.webAssembly);

const rootComponentManager = new WebRootComponentManager(options?.ssr?.circuitInactivityTimeoutMs ?? 2000);
const enhancedPageUpdateCallbacks = new CallbackCollection();

Blazor.registerEnhancedPageUpdateCallback = (callback) => enhancedPageUpdateCallbacks.registerCallback(callback);
const enqueueDispatchEnhancedLoad = createEnhancedLoadDispatcher();

const navigationEnhancementCallbacks: NavigationEnhancementCallbacks = {
documentUpdated: () => {
rootComponentManager.onDocumentUpdated();
enhancedPageUpdateCallbacks.enqueueCallbackInvocation();
enqueueDispatchEnhancedLoad();
},
};

Expand All @@ -68,6 +65,24 @@ function boot(options?: Partial<WebStartOptions>) : Promise<void> {
return Promise.resolve();
}

// This function ensures that 'enhancedload' only gets invoked once
// for any synchronous sequence of document updates via SSR.
function createEnhancedLoadDispatcher() {
let isDispatchPending = false;

return function() {
if (isDispatchPending) {
return;
}

isDispatchPending = true;
setTimeout(() => {
isDispatchPending = false;
Blazor._internal.dispatchEvent('enhancedload', {});
}, 0);
};
}

Blazor.start = boot;
window['DotNet'] = DotNet;

Expand Down
14 changes: 12 additions & 2 deletions src/Components/Web.JS/src/GlobalExports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { RootComponentsFunctions } from './Rendering/JSRootComponents';
import { attachWebRendererInterop } from './Rendering/WebRendererInteropMethods';
import { WebStartOptions } from './Platform/WebStartOptions';
import { RuntimeAPI } from 'dotnet';
import { EventRegistry } from './Services/EventRegistry';

// TODO: It's kind of hard to tell which .NET platform(s) some of these APIs are relevant to.
// It's important to know this information when dealing with the possibility of mulitple .NET platforms being available.
Expand All @@ -29,11 +30,12 @@ import { RuntimeAPI } from 'dotnet';
// * Blazor._internal.{foo}: internal, platform-agnostic Blazor APIs
// * Blazor.platform.{somePlatformName}.{foo}: public, platform-specific Blazor APIs (would be empty at first, so no initial breaking changes)
// * Blazor.platform.{somePlatformName}.{_internal}.{foo}: internal, platform-specific Blazor APIs
interface IBlazor {
export interface IBlazor {
navigateTo: (uri: string, options: NavigationOptions) => void;
registerCustomEventType: (eventName: string, options: EventTypeOptions) => void;

registerEnhancedPageUpdateCallback?: (callback: () => void) => { dispose(): void };
addEventListener: typeof EventRegistry.prototype.addEventListener;
removeEventListener: typeof EventRegistry.prototype.removeEventListener;
disconnect?: () => void;
reconnect?: (existingConnection?: HubConnection) => Promise<boolean>;
defaultReconnectionHandler?: DefaultReconnectionHandler;
Expand All @@ -47,6 +49,7 @@ interface IBlazor {
domWrapper: typeof domFunctions;
Virtualize: typeof Virtualize;
PageTitle: typeof PageTitle;
dispatchEvent: typeof EventRegistry.prototype.dispatchEvent;
forceCloseConnection?: () => Promise<void>;
InputFile?: typeof InputFile;
NavigationLock: typeof NavigationLock;
Expand Down Expand Up @@ -91,9 +94,13 @@ interface IBlazor {
}
}

const eventRegistry = new EventRegistry();

export const Blazor: IBlazor = {
navigateTo,
registerCustomEventType,
addEventListener: eventRegistry.addEventListener.bind(eventRegistry),
removeEventListener: eventRegistry.removeEventListener.bind(eventRegistry),
rootComponents: RootComponentsFunctions,
runtime: {} as RuntimeAPI,

Expand All @@ -106,8 +113,11 @@ export const Blazor: IBlazor = {
NavigationLock,
getJSDataStreamChunk: getNextChunk,
attachWebRendererInterop,
dispatchEvent: eventRegistry.dispatchEvent.bind(eventRegistry),
},
};

eventRegistry.attachBlazorInstance(Blazor);

// Make the following APIs available in global scope for invocation from JS
window['Blazor'] = Blazor;
35 changes: 0 additions & 35 deletions src/Components/Web.JS/src/Services/CallbackCollection.ts

This file was deleted.

66 changes: 66 additions & 0 deletions src/Components/Web.JS/src/Services/EventRegistry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

import { IBlazor } from '../GlobalExports';

// The base Blazor event type.
// Properties listed here get assigned by the event registry in 'dispatchEvent'.
interface BlazorEvent {
blazor: IBlazor;
type: keyof BlazorEventMap;
}

// Maps Blazor event names to the argument type passed to registered listeners.
export interface BlazorEventMap {
'enhancedload': BlazorEvent;
}

export class EventRegistry {
private readonly _eventListeners = new Map<string, Set<(ev: any) => void>>();

private _blazor: IBlazor | null = null;

public attachBlazorInstance(blazor: IBlazor) {
this._blazor = blazor;
}

public addEventListener<K extends keyof BlazorEventMap>(type: K, listener: (ev: BlazorEventMap[K]) => void): void {
let listenersForEventType = this._eventListeners.get(type);
if (!listenersForEventType) {
listenersForEventType = new Set();
this._eventListeners.set(type, listenersForEventType);
}

listenersForEventType.add(listener);
}

public removeEventListener<K extends keyof BlazorEventMap>(type: K, listener: (ev: BlazorEventMap[K]) => void): void {
const listenersForEventType = this._eventListeners.get(type);
if (!listenersForEventType) {
return;
}

listenersForEventType.delete(listener);
}

public dispatchEvent<K extends keyof BlazorEventMap>(type: K, ev: Omit<BlazorEventMap[K], keyof BlazorEvent>): void {
if (this._blazor === null) {
throw new Error('Blazor events cannot be dispatched until a Blazor instance gets attached');
}

const listenersForEventType = this._eventListeners.get(type);
if (!listenersForEventType) {
return;
}

const event: BlazorEventMap[K] = {
...ev,
blazor: this._blazor,
type,
};

for (const listener of listenersForEventType) {
listener(event);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,17 @@
preservedContent.textContent = 'Preserved content';
nonPreservedContent.textContent = 'Non preserved content';
let registration = null;
const onEnhancedLoad = (ev) => {
window.enhancedPageUpdateCount++;
}
window.listenForEnhancedUpdates = () => {
if (!registration) {
window.enhancedPageUpdateCount = 0;
registration = Blazor.registerEnhancedPageUpdateCallback(() => {
window.enhancedPageUpdateCount++;
});
}
window.enhancedPageUpdateCount = 0;
Blazor.addEventListener('enhancedload', onEnhancedLoad);
};
window.stopListeningForEnhancedUpdates = () => {
if (registration) {
registration.dispose();
registration = null;
}
Blazor.removeEventListener('enhancedload', onEnhancedLoad);
};
</script>

Expand Down

0 comments on commit b7bdce4

Please sign in to comment.