Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable global event dispatcher listeners to be lazily created. #17911

Closed
Closed
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
15 changes: 15 additions & 0 deletions packages/@ember/-internals/glimmer/lib/component-managers/curly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,19 @@ const EMPTY_POSITIONAL_ARGS: VersionedPathReference[] = [];

debugFreeze(EMPTY_POSITIONAL_ARGS);

function _setupLazyEventsForComponent(dispatcher: any, component: object) {
// non-interactive rendering (e.g. SSR) has no event dispatcher
if (dispatcher === undefined) { return; }

let lazyEvents = dispatcher._lazyEvents;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also needs to be "semi-public", in terms of @ember/jquery, see the other comment...


lazyEvents.forEach((mappedEventName: string, event: string) => {
if (mappedEventName !== null && typeof component[mappedEventName] === 'function') {
dispatcher.setupHandler(event, mappedEventName);
}
});
}

export default class CurlyComponentManager
extends AbstractManager<ComponentStateBucket, DefinitionState>
implements
Expand Down Expand Up @@ -311,6 +324,8 @@ export default class CurlyComponentManager
}
}

_setupLazyEventsForComponent(environment.eventDispatcher, component);

// Track additional lifecycle metadata about this component in a state bucket.
// Essentially we're saving off all the state we'll need in the future.
let bucket = new ComponentStateBucket(
Expand Down
2 changes: 1 addition & 1 deletion packages/@ember/-internals/glimmer/lib/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -735,7 +735,7 @@ const Component = CoreView.extend(
!this.renderer._destinedForDOM ||
!(() => {
let eventDispatcher = getOwner(this).lookup<any | undefined>('event_dispatcher:main');
let events = (eventDispatcher && eventDispatcher._finalEvents) || {};
let events = (eventDispatcher && eventDispatcher._finalEventNameMapping) || {};

// tslint:disable-next-line:forin
for (let key in events) {
Expand Down
5 changes: 5 additions & 0 deletions packages/@ember/-internals/glimmer/lib/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export default class Environment extends GlimmerEnvironment {

public debugStack: typeof DebugStack;
public inTransaction = false;
public eventDispatcher: any;

constructor(injections: any) {
super(injections);
Expand All @@ -49,6 +50,10 @@ export default class Environment extends GlimmerEnvironment {
if (DEBUG) {
this.debugStack = new DebugStack();
}

if (this.isInteractive) {
this.eventDispatcher = this.owner.lookup('event_dispatcher:main');
}
}

// this gets clobbered by installPlatformSpecificProtocolForURL
Expand Down
26 changes: 25 additions & 1 deletion packages/@ember/-internals/glimmer/lib/modifiers/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from '@glimmer/runtime';
import { Destroyable } from '@glimmer/util';
import { INVOKE } from '../utils/references';
import { Owner } from '@ember/-internals/owner';

const MODIFIERS = ['alt', 'shift', 'meta', 'ctrl'];
const POINTER_EVENT_TYPE_REGEX = /^click|mouse|touch/;
Expand Down Expand Up @@ -187,6 +188,23 @@ export class ActionState {

// implements ModifierManager<Action>
export default class ActionModifierManager implements ModifierManager<ActionState, Opaque> {
public owner: Owner;
private _setupEventHandler?: (eventName: string) => void;


constructor(owner: Owner) {
this.owner = owner;
}

get setupEventHandler(): (eventName: string) => void {
if (this._setupEventHandler === undefined) {
let dispatcher = this.owner.lookup('event_dispatcher:main');
this._setupEventHandler = (eventName) => dispatcher['setupHandler'](eventName);
}

return this._setupEventHandler;
}

create(
element: Simple.Element,
_state: Opaque,
Expand Down Expand Up @@ -246,6 +264,8 @@ export default class ActionModifierManager implements ModifierManager<ActionStat
install(actionState: ActionState) {
let { dom, element, actionId } = actionState;

this.setupEventHandler(actionState.eventName);

ActionHelper.registerAction(actionState);

dom.setAttribute(element, 'data-ember-action', '');
Expand All @@ -260,7 +280,11 @@ export default class ActionModifierManager implements ModifierManager<ActionStat
actionState.actionName = actionNameRef.value();
}

actionState.eventName = actionState.getEventName();
let newEventName = actionState.getEventName();
if (newEventName !== actionState.eventName) {
this.setupEventHandler(actionState.eventName);
actionState.eventName = actionState.getEventName();
}
}

getTag(actionState: ActionState) {
Expand Down
12 changes: 6 additions & 6 deletions packages/@ember/-internals/glimmer/lib/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,6 @@ const BUILTINS_HELPERS = {
'-assert-implicit-component-helper-argument': componentAssertionHelper,
};

const BUILTIN_MODIFIERS = {
action: { manager: new ActionModifierManager(), state: null },
};

export default class RuntimeResolver implements IRuntimeResolver<OwnedTemplateMeta> {
public compiler: LazyCompiler<OwnedTemplateMeta>;

Expand All @@ -102,7 +98,7 @@ export default class RuntimeResolver implements IRuntimeResolver<OwnedTemplateMe

private builtInModifiers: {
[name: string]: ModifierDefinition;
} = BUILTIN_MODIFIERS;
};

// supports directly imported late bound layouts on component.prototype.layout
private templateCache: Map<Owner, Map<TemplateFactory, OwnedTemplate>> = new Map();
Expand All @@ -114,10 +110,14 @@ export default class RuntimeResolver implements IRuntimeResolver<OwnedTemplateMe
public componentDefinitionCount = 0;
public helperDefinitionCount = 0;

constructor() {
constructor(owner: Owner) {
let macros = new Macros();
populateMacros(macros);
this.compiler = new LazyCompiler<OwnedTemplateMeta>(new CompileTimeLookup(this), this, macros);

this.builtInModifiers = {
action: { manager: new ActionModifierManager(owner), state: null },
};
}

/*** IRuntimeResolver ***/
Expand Down
7 changes: 5 additions & 2 deletions packages/@ember/-internals/glimmer/lib/template-compiler.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { Compiler } from '@glimmer/interfaces';
import RuntimeResolver from './resolver';
import { getOwner } from '@ember/-internals/owner';

// factory for DI
export default {
create(): Compiler {
return new RuntimeResolver().compiler;
create(props: any): Compiler {
let owner = getOwner(props);

return new RuntimeResolver(owner).compiler;
},
};
26 changes: 20 additions & 6 deletions packages/@ember/-internals/views/lib/system/event_dispatcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ export default EmberObject.extend({
);

this._eventHandlers = Object.create(null);
this._finalEventNameMapping = null;
this._sanitizedRootElement = null;
this._lazyEvents = new Map();
},

/**
Expand All @@ -142,7 +145,8 @@ export default EmberObject.extend({
@param addedEvents {Object}
*/
setup(addedEvents, _rootElement) {
let events = (this._finalEvents = assign({}, get(this, 'events'), addedEvents));
let events = (this._finalEventNameMapping = assign({}, get(this, 'events'), addedEvents));
let lazyEvents = this._lazyEvents;

if (_rootElement !== undefined && _rootElement !== null) {
set(this, 'rootElement', _rootElement);
Expand Down Expand Up @@ -216,9 +220,13 @@ export default EmberObject.extend({
}
}

// save off the final sanitized root element (for usage in setupHandler)
this._sanitizedRootElement = rootElement;

// setup event listeners for the non-lazily setup events
for (let event in events) {
if (events.hasOwnProperty(event)) {
this.setupHandler(rootElement, event, events[event]);
lazyEvents.set(event, events[event]);
}
}
},
Expand All @@ -234,12 +242,16 @@ export default EmberObject.extend({
@private
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change makes this method less "private" I think, at least "private" in the OOP-sense of being able to call it from outside (remains "private" in the semver-sense that it shouldn't be used in user-land). At least when @ember/jquery overrides the EventDispatcher with its jQuery-based implementation when Ember 4.0 will have dropped jQuery support, it has to properly support that method as it will be called from other places. Just to remind us of this... (see emberjs/ember-jquery#31)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya, unfortunately @private here is mostly serving as a marker for semver version of privateness. @ember/jquery is "first party" and will be supported regardless...

@method setupHandler
@param {Element} rootElement
@param {String} event the browser-originated event to listen to
@param {String} event the name of the event in the browser
@param {String} eventName the name of the method to call on the view
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The order of arguments has changed, and should be reflected in the DocBlock.

*/
setupHandler(rootElement, event, eventName) {
if (eventName === null) {
return;
setupHandler(
event,
eventName = this._finalEventNameMapping[event],
rootElement = this._sanitizedRootElement
) {
if (eventName === null || !this._lazyEvents.has(event)) {
return; // nothing to do
}

if (!JQUERY_INTEGRATION || jQueryDisabled) {
Expand Down Expand Up @@ -417,6 +429,8 @@ export default EmberObject.extend({
}
});
}

this._lazyEvents.delete(event);
},

destroy() {
Expand Down