Reputation: 1998
I have been working on an API for creating dynamic keyboard shortcuts that can work for any given Angular Component. Here is the model used to represent a Shortcut
:
export class Shortcut {
keyCode: number;
command: Function;
args?: any;
constructor(keyCode: number, command: Function, args?: any[]) {
this.keyCode = keyCode;
this.command = command;
if (args) {
this.args = args;
}
}
}
The intended use is as follows:
// function called in component OnInit function
initializeShortcuts() {
this.shortcutService
.addShortcut(
KeyCodes.One,
this.md.createMarkdown,
[this.mdEditor, '#']
);
}
In the component template, the keydown
event is checked against a textarea, which in turn calls a checkShortcut
function on the ShortcutService
. Here is the entire service:
import { Injectable } from '@angular/core';
import { Shortcut } from './shortcut';
@Injectable()
export class ShortcutService {
shortcuts = new Array<Shortcut>();
checkShortcut(event: KeyboardEvent) {
if (event.ctrlKey) {
const result = this.shortcuts.filter((s: Shortcut) => {
return s.keyCode === event.keyCode;
})[0];
if (result) {
console.log(result);
event.preventDefault();
result.args ? result.command(...result.args) : result.command();
}
}
}
addShortcut(keyCode: number, command: Function, args?: any[]) {
this.shortcuts.push(new Shortcut(keyCode, command, args));
}
removeShortcut(shortcut: Shortcut) {
const index = this.shortcuts.indexOf(shortcut);
this.shortcuts.splice(index, 1);
}
}
As I have it now, it works, but I have to explicitly define any supporting functions within the callback function itself as the this
context is unavailable, as seen in the MarkdownService
:
createMarkdown(editor: ElementRef, markdown: string) {
function getEditorValues(editor: ElementRef): EditorValues {
return new EditorValues(
editor.nativeElement.selectionStart,
editor.nativeElement.selectionEnd,
editor.nativeElement.value.length,
editor.nativeElement.value
);
}
const values = getEditorValues(editor);
if (values.start === values.end) {
editor.nativeElement.value = `${values.zeroToStart}${markdown}${values.startToLength}`;
} else {
editor.nativeElement.value = `${values.zeroToStart}${markdown}${values.startToLength}`;
}
}
If I define getEditorValues()
in the service itself and reference that function call when assigning the values
constant, an error is thrown because the object is undefined.
Is there a better approach to accomplishing something like this where dependent functionality can be accessed outside of the callback function?
See StackBlitz Project for example.
Upvotes: 2
Views: 2034
Reputation: 8470
You can keep the this
context of a function by wrapping it with an arrow function. Instead of passing this:
this.md.createMarkdown
Pass the following:
(...params) => this.md.createMarkdown(...params)
Or you can bind the this
instead by passing the following:
this.md.createMarkdown.bind(this)
Upvotes: 3