title: Angular Service Callback Command Composition
body: <h2>Setup</h2>
  
  <p>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 <code>Shortcut</code>:  </p>
  
  <pre><code>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;
      }
    }
  }
  </code></pre>
  
  <p>The intended use is as follows:  </p>
  
  <pre><code>// function called in component OnInit function
  initializeShortcuts() {
      this.shortcutService
          .addShortcut(
              KeyCodes.One, 
              this.md.createMarkdown, 
              [this.mdEditor, '#']
           );      
  }
  </code></pre>
  
  <p>In the component template, the <code>keydown</code> event is checked against a textarea, which in turn calls a <code>checkShortcut</code> function on the <code>ShortcutService</code>. Here is the entire service:  </p>
  
  <pre><code>import { Injectable } from '@angular/core';
  import { Shortcut } from './shortcut';
  
  
  @Injectable()
  export class ShortcutService {
    shortcuts = new Array&lt;Shortcut&gt;();
  
    checkShortcut(event: KeyboardEvent) {
      if (event.ctrlKey) {
        const result = this.shortcuts.filter((s: Shortcut) =&gt; {
          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);
    }
  }
  </code></pre>
  
  <h2>Problem</h2>
  
  <p>As I have it now, it works, but I have to explicitly define any supporting functions within the callback function itself as the <code>this</code> context is unavailable, as seen in the <code>MarkdownService</code>:  </p>
  
  <pre><code>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}`;
      }
  }  
  </code></pre>
  
  <p>If I define <code>getEditorValues()</code> in the service itself and reference that function call when assigning the <code>values</code> constant, an error is thrown because the object is undefined.</p>
  
  <p>Is there a better approach to accomplishing something like this where dependent functionality can be accessed outside of the callback function?  </p>
  
  <p>See <a href="https://stackblitz.com/edit/markdown-editor?file=app%2Fapp.component.ts" rel="nofollow noreferrer">StackBlitz Project</a> for example.</p>