3

Unable to access 'this' context inside addEventListener() in angular 2

In my rich text editor component, I am implementing image upload functionality. For that I have written an addEventListener event handler for 'change' in image input tag, I need to access an app service inside event listener via 'this'.

    Imageinput.addEventListener('change', () => {
                const file = Imageinput.files[0];
                var a = this;           

                if (Imageinput.files != null && Imageinput.files[0] != null) {
                    debugger;     
                    this._imageUploadService.uploadImageToBlob(file).subscribe((res) => {
                        debugger;
                        this._returnedURL = res.imageUrl;
                        this.pushImageToEditor();
                    });               
                }
            }
            );

but this._imageUploadService returns undefined everytime,even though console don't have any error.


Here is my complete component.ts code -
    export class CreateArticleComponent extends AppComponentBase {

        @ViewChild("fileInput") fileInput;

        public editor;
        public _returnedURL = "";    

        constructor(
            injector: Injector,
            private _imageUploadService: ArticleServiceProxy,
        ) {
            super(injector);
        }

        onEditorBlured(quill) {
            console.log('editor blur!', quill);
        }

        onEditorFocused(quill) {
            console.log('editor focus!', quill);
        }

        onEditorCreated(quill) {
            console.log(quill);
            let toolbar = quill.getModule('toolbar');
            toolbar.addHandler('image', this.imageHandler);        

            //this.editorContent = quill;
            this.editor = quill;
            console.log('quill is ready! this is current quill instance object', quill);
        }

        imageHandler() {           
            debugger;
            let self = this;

            const Imageinput = document.createElement('input');
            Imageinput.setAttribute('type', 'file');
            //Imageinput.setAttribute('name', 'articleImage');
            Imageinput.setAttribute('accept', 'image/png, image/gif, image/jpeg, image/bmp, image/x-icon');
            Imageinput.classList.add('ql-image');


            Imageinput.addEventListener('change', () => {
                const file = Imageinput.files[0];

                if (Imageinput.files != null && Imageinput.files[0] != null) {
                    debugger;
this._imageUploadService.uploadImageToBlob(file).subscribe((res) => {
                        debugger;
                        this._returnedURL = res.imageUrl;
                        this.pushImageToEditor();
                    });                   
                }
            }
            );

            Imageinput.click();
        }

        SendFileToServer(file: any) {
            debugger;
            this._imageUploadService.uploadImageToBlob(file).subscribe((res) => {
                debugger;
                this._returnedURL = res.imageUrl;
                this.pushImageToEditor();
            });
        }

        pushImageToEditor() {
            debugger;
            const range = this.editor.getSelection(true);
            const index = range.index + range.length;
            this.editor.insertEmbed(range.index, 'image', this._returnedURL);
        }

        ngAfterViewInit() {           
        }
    }    

Here is my editor HTML -

  <quill-editor #fileInput                                  
            [(ngModel)]="editorContent"
            [ngModelOptions]="{standalone: true}"
            (onEditorCreated)="onEditorCreated($event)"
            (onContentChanged)="onContentChanged($event)"
            (onSelectionChanged)="logSelection($event)"
            [style]="{'height':'300px'}">
</quill-editor>

I can access this._imageUploadService in other methods but not able to access it inside addEventListener().Any help will be appreciated

Amit Sawant
  • 35
  • 1
  • 5
  • What exactly do you expect `this` to be? Since you're using arrow function, it will be same both inside and addEventListener callback. – Estus Flask Feb 26 '18 at 10:47
  • Do you actually have any error when not using the debugger? If not, it might not be that the debugger is getting confused with the scope, I've had that sometimes happen to me – David Feb 26 '18 at 10:54
  • you can use angular Renderer2 to use the context of the class inside the event listener, and its a much preffered way to create your Input element aswell. – Daniel Netzer Feb 26 '18 at 11:56

2 Answers2

4

In change event handler this refers to toolbar.addHandler's context, so you need it to bind this.imageHandler like this

  onEditorCreated(quill) {
        console.log(quill);
        let toolbar = quill.getModule('toolbar');
        toolbar.addHandler('image', this.imageHandler.bind(this));  // <--      

        //this.editorContent = quill;
        this.editor = quill;
        console.log('quill is ready! this is current quill instance object', quill);
    }
El houcine bougarfaoui
  • 35,805
  • 9
  • 37
  • 37
  • Thanks buddy,you are right.I was trying to find a way to use .bind(this) but didn't find a way to do that.Now 'this' is accessible in event listener also. I wasted 2 days in finding solution. #Thanks alot again – Amit Sawant Feb 26 '18 at 11:19
1

instead of using .bind or trying to force objects/context into callbacks you can simply use angular Renderer2 wrapper that will give you access to the Component class context.

    imageHandler() {
    debugger;

    const Imageinput = this.renderer.createElement('input');
    this.renderer.setAttribute(Imageinput, 'type', 'file');
    this.renderer.setAttribute(Imageinput, 'accept', 'image/png, image/gif, image/jpeg, image/bmp, image/x-icon');
    this.renderer.addClass(Imageinput, 'ql-image');

    this.renderer.listen(Imageinput, 'change', () => {
      if (Imageinput.files != null && Imageinput.files[0] != null) {
        debugger;
        this._imageUploadService.uploadImageToBlob(file).subscribe((res) => {
          debugger;
          this._returnedURL = res.imageUrl;
          this.pushImageToEditor();
        });
      }
    });

    Imageinput.click();
  }
Daniel Netzer
  • 2,151
  • 14
  • 22
  • That is also an correct way of using angular's renderer.But I am new to angular 2,So I was not aware of using it.I will definitely try this and inform you. #Thanks Daniel – Amit Sawant Feb 26 '18 at 12:11
  • 1
    you're welcome, always try to stay away from the document and window objects as much as possible because once you want to go Universal application its blocking. you're welcome. i've tested most of the code it should work. – Daniel Netzer Feb 26 '18 at 12:14