title: Angular 8 - Resizing image on the front-end change-detection issue
body: <p>I am trying to build an image compressor service from a couple of tutorials I found online . The <strong><em>service</em></strong> itself works as expected , it receives an image as File , then it compresses it and returns an <strong><em>Observable</em></strong> .
  All works great , except that I want to use the compressed image in my component , before uploading it to the server . </p>
  
  <p>The component doesn't detect when a new compressed image has arrived through the <strong><em>async</em></strong> pipe. If I subscribe manually to the Observable , I get the image data as expected , but if I try to update a component property with it , it doesn't immediately change the view , but rather changes it with the old 'image data' if I try to compress a new Image .</p>
  
  <p>I found that this problem might occur if part of the code resolves outside the ngZone , so I found a workaround ( see below in the code ) with injecting <strong><em>ApplicationRef</em></strong> and using <strong><em>.tick()</em></strong>
  which actually works great , but makes my service hardly reusable .</p>
  
  <p><strong>My question is :</strong> 
  Which part of the service code runs outside of ngZone and what are the possible fixes or workarounds so the service is reusable in other components , without having to inject ApplicationRef and .tick() everytime the service emits data .</p>
  
  <p>Here is my <strong><em>service code</em></strong> : </p>
  
  <pre><code> import { Observable ,  Subscriber } from 'rxjs';
  import { Injectable } from '@angular/core';
  import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
  
  @Injectable({
    providedIn: 'root'
  })
  
  export class ImageCompressorService {
  
  // globals
  private _currentFile : File ;
  private _currentImage : ICompressedImage = {} ;
  
  // Constructor
  constructor( private sanitizer : DomSanitizer) {}
  
  // FileReader Onload callback
  readerOnload(observer : Subscriber&lt;ICompressedImage&gt;)  {
   return (progressEvent : ProgressEvent) =&gt; {
    const img = new Image();
    img.src = (progressEvent.target as any).result;
    img.onload = this.imageOnload(img , observer);
  }
  }
  
  // Image Onload callback
   imageOnload(image : HTMLImageElement , observer : Subscriber&lt;ICompressedImage&gt;) {
    return () =&gt; {
    const canvas = document.createElement('canvas');
    canvas.width = 100;
    canvas.height = 100;
    const context = &lt;CanvasRenderingContext2D&gt;canvas.getContext('2d');
    context.drawImage(image , 0 , 0 , 100 , 100);
    this.toICompressedImage(context , observer);
  }}
  
  // Emit CompressedImage
  toICompressedImage(context : CanvasRenderingContext2D , observer : Subscriber&lt;ICompressedImage&gt; ) {
    context.canvas.toBlob(
      (blob) =&gt; {
        this._currentImage.blob = blob ;
        this._currentImage.image = new File([blob] , this._currentFile.name , {type : 'image/jpeg', lastModified : Date.now()} );
        this._currentImage.imgUrl = this.sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(blob));
        this._currentImage.name = this._currentFile.name ;
        observer.next(this._currentImage);
        observer.complete();
      } ,
      'image/jpeg' ,
      1
    );
  }
  
  //  Compress function
   compress(file : File) : Observable&lt;ICompressedImage&gt; {
     this._currentFile = file ;
     return new Observable&lt;ICompressedImage&gt;(
       observer =&gt; {
         const currentFile = file;
         const reader = new FileReader();
         reader.readAsDataURL(currentFile);
         reader.onload = this.readerOnload(observer);
       }
     );
   }
  }
  
  // Image Data Interface
  export interface ICompressedImage {
    name? : string;
    image? : File ;
    blob? : Blob ;
    imgUrl? : SafeUrl ;
  }
  </code></pre>
  
  <p>And this is my <strong><em>component.ts</em></strong> : </p>
  
  <pre><code>import { Component, OnInit, ApplicationRef } from '@angular/core';
  import { ImageCompressorService, ICompressedImage } from 'src/app/shared/services/image-compressor.service';
  
  
  @Component({
    selector: 'app-new-project',
    templateUrl: './new-project.component.html',
    styleUrls: ['./new-project.component.css']
  })
  export class NewProjectComponent implements OnInit  {
  // globals
  private selectedImage ;
  compressedImage :  ICompressedImage = {name : 'No file selected'};
  
  
  // Constructor
    constructor( private compressor : ImageCompressorService,
                 private ar : ApplicationRef
               ) {}
  // OnInit implementation
       ngOnInit(): void {}
  
  // Compress method
    compress(fl : FileList) {
  if (fl.length&gt;0) {
      this.selectedImage = fl.item(0);
      this.compressor
      .compress(this.selectedImage)
      .subscribe(data =&gt; {
       this.compressedImage = data ;
       this.ar.tick();
      });
    } else {
      console.error('No file/s selected');
  
    }
    }
  
  
  }
  </code></pre>
  
  <p>Here is my <strong><em>HTML template</em></strong> for the component : </p>
  
  <pre><code>&lt;div style='border : 1px solid green;'&gt;
      &lt;input type='file' #SelectedFile (change)="compress($event.target.files)" accept='image/*' &gt;
  &lt;/div&gt;
  
  
  &lt;div
  style = 'border : 1px solid blue ; height : 200px;'
  *ngIf="compressedImage " &gt;
   &lt;strong&gt;File Name : &lt;/strong&gt;{{ compressedImage?.name }}
  
  &lt;img *ngIf="compressedImage?.imgUrl as src"
  [src]= 'src' &gt;
  &lt;/div&gt;
  </code></pre>
  
  <p>The way I have showed my code , it works perfect . Try commenting out 
   <strong>this.ar.tick();</strong> in <strong><em>Compress Method</em></strong> of the <strong><em>component.ts</em></strong> file and see the change .</p>