title: Change detection doesnt work with file upload
body: <p>I have a Angular 8 application and I have a form. But I want to detect a change when a user selects a new image. But nothing happened</p>
  <p>Googled, following tutorials.</p>
  <p>So this is the html template file:</p>
  <pre><code>  &lt;app-vital10-page [noTopBar]=&quot;true&quot;&gt;
      &lt;div class=&quot;settings-account backslab&quot;&gt;
        &lt;main class=&quot;settings-account-main&quot;&gt;
          &lt;form class=&quot;form settings-account-form&quot; *ngIf=&quot;profile&quot; #form=&quot;ngForm&quot; (ngSubmit)=&quot;save(form)&quot; id=&quot;form-settings-account&quot;&gt;
  
            &lt;h2 class=&quot;page-title settings-account-title&quot;&gt;Account&lt;/h2&gt;
  
            &lt;div *ngIf=&quot;formErrors &amp;&amp; formErrors.length &gt; 0&quot; class=&quot;data-entry&quot; [@errors]&gt;
              &lt;div class=&quot;error-box&quot;&gt;
                &lt;div class=&quot;error-box-close&quot; (click)=&quot;clearErrors()&quot;&gt;X&lt;/div&gt;
                &lt;div *ngFor=&quot;let errorField of formErrors&quot;&gt;
                  &lt;h4 class=&quot;error-field&quot;&gt;{{ getLabel(errorField.header) }}&lt;/h4&gt;
                  &lt;ul class=&quot;error-list&quot;&gt;
                    &lt;li class=&quot;error-message&quot; *ngFor=&quot;let errorMessage of errorField.messages&quot;&gt;{{ errorMessage }}&lt;/li&gt;
                  &lt;/ul&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;
  
            &lt;div class=&quot;settings-account-body&quot;&gt;
              &lt;section class=&quot;settings-account-form-form&quot;&gt;
  
                &lt;div class=&quot;row&quot;&gt;
                  &lt;div class=&quot;input-group col-4&quot;&gt;
                    &lt;label for=&quot;firstName&quot;&gt;{{ getLabel('firstName') }}&lt;/label&gt;
                    &lt;input type=&quot;text&quot; id=&quot;firstName&quot; name=&quot;firstName&quot; [(ngModel)]=&quot;profile.firstName&quot; (blur)=&quot;setInitials()&quot;&gt;
                  &lt;/div&gt;
  
                  &lt;div class=&quot;input-group col-2&quot;&gt;
                    &lt;label for=&quot;lastNamePrefix&quot;&gt;{{ getLabel('lastNamePrefix') }}&lt;/label&gt;
                    &lt;input type=&quot;text&quot; id=&quot;lastNamePrefix&quot; name=&quot;lastNamePrefix&quot; [(ngModel)]=&quot;profile.lastNamePrefix&quot;&gt;
                  &lt;/div&gt;
  
                  &lt;div class=&quot;input-group col-6&quot;&gt;
                    &lt;label for=&quot;lastName&quot;&gt;{{ getLabel('lastName') }}&lt;/label&gt;
                    &lt;input type=&quot;text&quot; id=&quot;lastName&quot; name=&quot;lastName&quot; [(ngModel)]=&quot;profile.lastName&quot; (blur)=&quot;setInitials()&quot;&gt;
                  &lt;/div&gt;
  
                &lt;/div&gt;
  
                &lt;div class=&quot;row&quot;&gt;
  
                  &lt;div class=&quot;input-group col-4&quot;&gt;
                    &lt;label for=&quot;marriedName&quot;&gt;{{ getLabel('marriedName') }}&lt;/label&gt;
                    &lt;input type=&quot;text&quot; id=&quot;marriedName&quot; name=&quot;marriedName&quot; [(ngModel)]=&quot;profile.marriedName&quot;&gt;
                  &lt;/div&gt;
  
                  &lt;div class=&quot;input-group col-2&quot;&gt;
                    &lt;label for=&quot;initials&quot;&gt;{{ getLabel('initials') }}&lt;/label&gt;
                    &lt;input type=&quot;text&quot; id=&quot;initials&quot; name=&quot;initials&quot; [(ngModel)]=&quot;profile.initials&quot;&gt;
                  &lt;/div&gt;
  
                  &lt;div class=&quot;input-group col-3&quot;&gt;
                    &lt;label for=&quot;gender&quot;&gt;{{ getLabel('gender') }}&lt;/label&gt;
                    &lt;select name=&quot;gender&quot; id=&quot;gender&quot; [(ngModel)]=&quot;profile.gender&quot;&gt;
                      &lt;option *ngFor=&quot;let type of genders&quot; [value]=&quot;type.value&quot;&gt;{{ type.text }}&lt;/option&gt;
                    &lt;/select&gt;
                  &lt;/div&gt;
  
                  &lt;div class=&quot;input-group col-3&quot;&gt;
                    &lt;label for=&quot;dateOfBirth&quot;&gt;{{ getLabel('dateOfBirth') }}&lt;/label&gt;
                    &lt;input type=&quot;text&quot; id=&quot;dateOfBirth&quot; name=&quot;dateOfBirth&quot; [value]=&quot;profile.dateOfBirth | date:'dd-MM-yyyy'&quot; readonly tabindex=&quot;-1&quot;&gt;
                  &lt;/div&gt;
  
                &lt;/div&gt;
  
                &lt;div class=&quot;row&quot;&gt;
  
                  &lt;div class=&quot;input-group col-8&quot;&gt;
                    &lt;label for=&quot;street&quot;&gt;{{ getLabel('street') }}&lt;/label&gt;
                    &lt;input type=&quot;text&quot; id=&quot;street&quot; name=&quot;street&quot; [(ngModel)]=&quot;profile.street&quot;&gt;
                  &lt;/div&gt;
  
                  &lt;div class=&quot;input-group col-2&quot;&gt;
                    &lt;label for=&quot;houseNumber&quot; [attr.abbreviation]=&quot;getLabelAbbreviation('houseNumber')&quot;&gt;{{ getLabel('houseNumber') }}&lt;/label&gt;
                    &lt;input type=&quot;text&quot; id=&quot;houseNumber&quot; name=&quot;houseNumber&quot; [(ngModel)]=&quot;profile.houseNumber&quot;&gt;
                  &lt;/div&gt;
  
                  &lt;div class=&quot;input-group col-2&quot;&gt;
                    &lt;label for=&quot;houseExtension&quot; [attr.abbreviation]=&quot;getLabelAbbreviation('houseExtension')&quot;&gt;{{ getLabel('houseExtension') }}&lt;/label&gt;
                    &lt;input type=&quot;text&quot; id=&quot;houseExtension&quot; name=&quot;houseExtension&quot; [(ngModel)]=&quot;profile.houseExtension&quot;&gt;
                  &lt;/div&gt;
  
                &lt;/div&gt;
  
                &lt;div class=&quot;row&quot;&gt;
  
                  &lt;div class=&quot;input-group col-4&quot;&gt;
                    &lt;label for=&quot;zipCode&quot;&gt;{{ getLabel('zipCode') }}&lt;/label&gt;
                    &lt;input type=&quot;text&quot; id=&quot;zipCode&quot; name=&quot;zipCode&quot; [(ngModel)]=&quot;profile.zipCode&quot;&gt;
                  &lt;/div&gt;
  
                  &lt;div class=&quot;input-group col-8&quot;&gt;
                    &lt;label for=&quot;city&quot;&gt;{{ getLabel('city') }}&lt;/label&gt;
                    &lt;input type=&quot;text&quot; id=&quot;city&quot; name=&quot;city&quot; [(ngModel)]=&quot;profile.city&quot;&gt;
                  &lt;/div&gt;
  
                &lt;/div&gt;
  
                &lt;div class=&quot;row&quot;&gt;
  
                  &lt;div class=&quot;input-group col-4&quot;&gt;
                    &lt;label for=&quot;email&quot;&gt;{{ getLabel('email') }}&lt;/label&gt;
                    &lt;input type=&quot;email&quot; id=&quot;email&quot; name=&quot;email&quot; [ngModel]=&quot;profile.email&quot; readonly tabindex=&quot;-1&quot;&gt;
                  &lt;/div&gt;
  
                  &lt;div class=&quot;input-group col-4&quot;&gt;
                    &lt;label for=&quot;telephoneNr&quot; [attr.abbreviation]=&quot;getLabelAbbreviation('telephoneNr')&quot;&gt;{{ getLabel('telephoneNr') }}&lt;/label&gt;
                    &lt;input type=&quot;tel&quot; id=&quot;telephoneNr&quot; name=&quot;telephoneNr&quot; [(ngModel)]=&quot;profile.telephoneNr&quot;&gt;
                  &lt;/div&gt;
  
                  &lt;div class=&quot;input-group col-4&quot;&gt;
                    &lt;label for=&quot;mobileNr&quot; [attr.abbreviation]=&quot;getLabelAbbreviation('mobileNr')&quot;&gt;{{ getLabel('mobileNr') }}&lt;/label&gt;
                    &lt;input type=&quot;tel&quot; id=&quot;mobileNr&quot; name=&quot;mobileNr&quot; [(ngModel)]=&quot;profile.mobileNr&quot;&gt;
                  &lt;/div&gt;
                &lt;/div&gt;
  
                &lt;div class=&quot;profile-save&quot;&gt;
                  &lt;button class=&quot;button profile-save-button&quot;  type=&quot;submit&quot; [disabled] = &quot;!form.dirty&quot; &gt;Wijzigingen opslaan&lt;/button&gt;
                  &lt;span *ngIf=&quot;saving&quot; class=&quot;fa fa-refresh fa-spin fa-2x profile-save-icon&quot;&gt;&lt;/span&gt;
                &lt;/div&gt;
              &lt;/section&gt;
  
              &lt;section class=&quot;settings-account-form-photo&quot;&gt;
  
                &lt;img [src]=&quot;currentPicture()&quot; class=&quot;photo-upload&quot; alt=&quot;&quot;&gt;
                &lt;input #fileinput type=&quot;file&quot; name=&quot;photo&quot; id=&quot;photo&quot; class=&quot;photo-input&quot; accept=&quot;image/*&quot;
                 (change)=&quot;loadImage($event, form)&quot;&gt;
                &lt;div class=&quot;photo-selection&quot;&gt;
                  &lt;span [ngClass]=&quot;{'delete-picture': deletePicture}&quot;&gt;
  
                  &lt;/span&gt;
                &lt;/div&gt;
                &lt;div class=&quot;form-photo-actions&quot;&gt;
                  &lt;button type=&quot;button&quot; class=&quot;button button-inline photo-button&quot; (click)=&quot;fileinput.click()&quot;&gt;Kies foto&lt;/button&gt;
                  &lt;button type=&quot;button&quot; class=&quot;button button-inline photo-button&quot; (click)=&quot;toggleRemove(form)&quot;&gt;
                    {{ !deletePicture ? 'Verwijder foto' : 'Behoud foto' }}
                  &lt;/button&gt;
                &lt;/div&gt;
                &lt;input type=&quot;hidden&quot; name=&quot;picture&quot; id=&quot;picture&quot; ngModel=&quot;{{ profile.picture }}&quot;&gt;
                &lt;input type=&quot;hidden&quot; name=&quot;uploadPicture&quot; id=&quot;uploadPicture&quot; [ngModel]=&quot;profile.uploadPicture&quot;&gt;
              &lt;/section&gt;
            &lt;/div&gt;
  
          &lt;/form&gt;
        &lt;/main&gt;
        &lt;footer class=&quot;settings-account-footer&quot;&gt;
          Lees hier nogmaals de
          &lt;a href=&quot;https://www.vital10.nl/algemene-voorwaarden-deelnemers/&quot; [target]=&quot;inApp.getATagBlankTarget()&quot; rel=&quot;noopener noreferrer&quot; class=&quot;link-clear&quot;&gt;algemene voorwaarden&lt;/a&gt; en de
          &lt;a href=&quot;https://www.vital10.nl/privacyverklaring/&quot; [target]=&quot;inApp.getATagBlankTarget()&quot; rel=&quot;noopener noreferrer&quot; class=&quot;link-clear&quot;&gt;Privacyverklaring&lt;/a&gt; van MijnVital10.
        &lt;/footer&gt;
      &lt;/div&gt;
    &lt;/app-vital10-page&gt;
  
  
  </code></pre>
  <p>And this is the javascript file:</p>
  <pre><code>import { Component, NgZone, OnDestroy, OnInit, ViewChild, ChangeDetectorRef, ChangeDetectionStrategy } from '@angular/core';
  import { NgForm } from '@angular/forms';
  import { Subscription } from 'rxjs';
  import { AccountProfile } from '../../interfaces/profile/account-profile.interface';
  import { errors } from '../../measurement/measurement-form/measurement-form.animations';
  import { HealthAPIService } from '../../shared/health-api/health-api.service';
  import { InAppService } from 'app/shared/in-app/in-app.service';
  import { FormCanDeactivate } from 'app/shared/form/form-can-deactivate/form-can-deactivate';
  
  @Component( {
    selector   : 'app-settings-account',
    templateUrl: './settings-account.component.html',
    animations : [ errors ],
  
  } )
  export class SettingsAccountComponent extends FormCanDeactivate implements OnInit, OnDestroy  {
    private profileSubscription: Subscription;
    profile: AccountProfile;
    currentProfile: AccountProfile;
    selectedFileName: string;
    deletePicture: boolean;
    saving: boolean;
    formErrors: { header: string; messages: string[] }[];
    innerWidth = window.innerWidth;
  
  
    @ViewChild( 'form', {static: false} )
    form: NgForm;
  
  
    genders: { value: string; text: string }[];
  
    labels = {
      firstName     : {
        nl: 'Voornaam'
      },
      lastNamePrefix: {
        nl: 'Tussenv.'
      },
      lastName      : {
        nl: 'Achternaam'
      },
      marriedName   : {
        nl: 'Meisjesnaam'
      },
      initials      : {
        nl: 'Initialen'
      },
      gender        : {
        nl: 'Geslacht'
      },
      dateOfBirth   : {
        nl: 'Geboren'
      },
      street        : {
        nl: 'Straat'
      },
      houseNumber   : {
        nl: 'Huis nr.',
        nl_abbreviation: 'Nr.'
      },
      houseExtension: {
        nl: 'Toevoeging',
        nl_abbreviation: 'Ext.'
      },
      zipCode       : {
        nl: 'Postcode'
      },
      city          : {
        nl: 'Stad'
      },
      email         : {
        nl: 'Email'
      },
      telephoneNr   : {
        nl: 'Telefoon nr.',
        nl_abbreviation: 'Tel. nr.'
      },
      mobileNr      : {
        nl: 'Mobiel nr.',
        nl_abbreviation: 'Mobiel nr.'
      },
      photo         : {
        nl: 'Foto'
      }
    };
  
    inApp: InAppService;
  
    constructor(
      private healthAPI: HealthAPIService,
      private ngZone: NgZone,
      private cd: ChangeDetectorRef,
      private inAppService: InAppService
       ) {
      super();
      this.saving = false;
      this.genders = [
        {
          value: 'Unknown',
          text : 'Onbekend'
        },
        {
          value: 'Male',
          text : 'Man'
        },
        {
          value: 'Female',
          text : 'Vrouw'
        }
      ];
      this.inApp = inAppService;
      this.cd.markForCheck();
    }
  
    ngOnInit() {
      this.innerWidth = window.innerWidth;
      this.profileSubscription = this.healthAPI.getProfile()
                                     .subscribe( ( profile: AccountProfile ) =&gt; {
                                       this.profile = profile;
                                       this.deletePicture = false;
                                       if ( this.form ) {
                                         if ( this.form.controls.picture ) {
                                           this.form.controls.picture.setValue( this.profile.picture );
  
                                           //console.log(this.cd.detectChanges());
                                         }
                                         if ( this.form.controls.uploadPicture ) {
                                           this.form.controls.uploadPicture.setValue( false );
                                         }
                                       }
                                     } );
    }
  
    public ngOnDestroy(): void {
      if ( this.profileSubscription ) {
        this.profileSubscription.unsubscribe();
      }
    }
  
    currentPicture() {
      let pictureValue;
      if ( this.form &amp;&amp; this.form.controls.picture ) {
        pictureValue = this.form.controls.picture.value;
      } else {
        pictureValue = this.profile.picture;
      }
      if ( pictureValue ) {
        return 'data:image/png;base64,' + pictureValue;
      }
      return 'assets/img/default_avatar.png';
    }
  
    clearErrors() {
      this.initErrorLog();
    }
  
    initErrorLog() {
      this.formErrors = [];
    }
  
    // Parses errorlog that comes from the server into a format that's easily used by the template
    parseErrorLog( errorLog ) {
      // Reset errorlog
      this.initErrorLog();
  
      for ( const header in errorLog ) {
        if ( Object.prototype.hasOwnProperty.call( errorLog, header ) ) {
          const inputError = {
            header,
            messages: errorLog[ header ]
          };
          this.formErrors.push( inputError );
        }
      }
    }
  
    save() {
      this.saving = true;
      const commonFunc = () =&gt; {
        this.ngZone.runOutsideAngular( () =&gt; {
          window.setTimeout( () =&gt; {
            this.ngZone.run( () =&gt; {
              this.saving = false; // Wait one second for clarity
            } );
          }, 1000 );
        } );
      };
      this.healthAPI.putProfile( this.form.value )
          .subscribe(
            res =&gt; {
              this.formErrors = undefined;
              commonFunc();
            },
            err =&gt; {
              this.parseErrorLog( JSON.parse( err._body ) );
              commonFunc();
            }
          );
    }
  
    getInitials(): string {
      return this.profile.firstName.charAt( 0 )
                 .toUpperCase() + '.' + this.profile.lastName.charAt( 0 )
                                            .toUpperCase() + '.';
    }
  
    setInitials() {
      if ( this.profile.firstName &amp;&amp; this.profile.lastName ) {
        this.profile.initials = this.getInitials();
      }
    }
  
    toggleRemove() {
      this.deletePicture = !this.deletePicture;
  
      if ( this.deletePicture ) {
        this.selectedFileName = null;
        this.form.controls.picture.setValue( null );
        this.form.controls.uploadPicture.setValue( true );
      } else {
        this.form.controls.picture.setValue( this.profile.picture );
        this.form.controls.uploadPicture.setValue( false );
      }
    }
  
    loadImage( event: Event ) {
  
      this.cd.markForCheck();
      if (!this.cd['destroyed']) {
        console.log(this.cd.detectChanges());
      }
  
      const fileInput = event.target as HTMLInputElement;
      this.selectedFileName = fileInput.files[ 0 ].name;
      if ( fileInput.files[ 0 ] ) {
        const fileReader = new FileReader();
       //console.log(this.cd.markForCheck());
  
        fileReader.addEventListener( 'load', () =&gt; {
          const base64Img = (fileReader.result as string).substring((fileReader.result as string).indexOf( ',' ) + 1 );
          this.form.controls.picture.setValue( base64Img );
          this.form.controls.uploadPicture.setValue( true );
      //    console.log('image');
        } );
        fileReader.readAsDataURL( fileInput.files[ 0 ] );
        this.cd.markForCheck();
      } else {
        this.form.controls.picture.setValue( this.profile.picture );
        this.form.controls.uploadPicture.setValue( false );
  
      }
    }
  
    getLabel( label: string, locale: string = 'nl' ): string {
      const normalizedLabel = label.charAt( 0 )
                                   .toLowerCase() + label.slice( 1 );
      if ( !this.labels[ normalizedLabel ] ) {
        throw new Error( `Unknown label ${normalizedLabel} requested in settings-account component` );
      }
      return this.labels[ normalizedLabel ][ locale ];
    }
    getLabelAbbreviation( label: string, locale: string = 'nl' ): string {
      const normalizedLabel = label.charAt( 0 )
                                   .toLowerCase() + label.slice( 1 );
      if ( !this.labels[ normalizedLabel ] ) {
        throw new Error( `Unknown label ${normalizedLabel} requested in settings-account component` );
      }
      if ( !this.labels[ normalizedLabel ][ locale + '_abbreviation' ] ) {
        throw new Error( `Unknown label ${normalizedLabel} requested in settings-account component` );
      }
      return this.labels[ normalizedLabel ][ locale + '_abbreviation' ];
    }
  }
  
  
  </code></pre>
  <p>And so this is the functionality for uploading new file:</p>
  <pre><code>loadImage( event: Event ) {
  
      this.cd.markForCheck();
      if (!this.cd['destroyed']) {
        console.log(this.cd.detectChanges());
      }
  
      const fileInput = event.target as HTMLInputElement;
      this.selectedFileName = fileInput.files[ 0 ].name;
      if ( fileInput.files[ 0 ] ) {
        const fileReader = new FileReader();
       //console.log(this.cd.markForCheck());
  
        fileReader.addEventListener( 'load', () =&gt; {
          const base64Img = (fileReader.result as string).substring((fileReader.result as string).indexOf( ',' ) + 1 );
          this.form.controls.picture.setValue( base64Img );
          this.form.controls.uploadPicture.setValue( true );
      //    console.log('image');
        } );
        fileReader.readAsDataURL( fileInput.files[ 0 ] );
        this.cd.markForCheck();
      } else {
        this.form.controls.picture.setValue( this.profile.picture );
        this.form.controls.uploadPicture.setValue( false );
  
      }
    }
  
  </code></pre>
  <p>That with changing file the form will be dirty.</p>
  <p>Thank you</p>
  <p>You mean like this:</p>
  <pre><code>loadImage( event: Event ) {
  
      this.cd.markForCheck();
      if (!this.cd['destroyed']) {
     //   console.log(this.cd.detectChanges());
      }
  
      const fileInput = event.target as HTMLInputElement;
      this.selectedFileName = fileInput.files[ 0 ].name;
      if ( fileInput.files[ 0 ] ) {
        const fileReader = new FileReader();
  
  
        fileReader.onload = _event =&gt; {
        console.log(this.cd.markForCheck());
        };
  
        fileReader.addEventListener( 'load', () =&gt; {
          const base64Img = (fileReader.result as string).substring((fileReader.result as string).indexOf( ',' ) + 1 );
          this.form.controls.picture.setValue( base64Img );
          this.form.controls.uploadPicture.setValue( true );
      //    console.log('image');
        } );
        fileReader.readAsDataURL( fileInput.files[ 0 ] );
        this.cd.markForCheck();
      } else {
        this.form.controls.picture.setValue( this.profile.picture );
        this.form.controls.uploadPicture.setValue( false );
  
      }
    }
  </code></pre>
  <p>I also tried like this:</p>
  <pre><code>loadImage( event: Event ) {
  
  
      const fileInput = event.target as HTMLInputElement;
      this.selectedFileName = fileInput.files[ 0 ].name;
      if ( fileInput.files[ 0 ] ) {
        const fileReader = new FileReader();
  
  
        fileReader.onload = (_event: Event ) =&gt; {
          const base64Img = (fileReader.result as string).substring((fileReader.result as string).indexOf( ',' ) + 1 );
          this.form.controls.picture.setValue( base64Img );
          this.form.controls.uploadPicture.setValue( true );
  
          this.cd.markForCheck();
        };
  
        fileReader.readAsDataURL( fileInput.files[ 0 ] );
        this.cd.markForCheck();
      } else {
        this.form.controls.picture.setValue( this.profile.picture );
        this.form.controls.uploadPicture.setValue( false );
  
      }
    }
  </code></pre>
  <p>I have it now like this:</p>
  <pre><code>loadImage( event: Event ) {
  
      const fileInput = event.target as HTMLInputElement;
      this.selectedFileName = fileInput.files[ 0 ].name;
      if ( fileInput.files[ 0 ] ) {
        const fileReader = new FileReader();
  
  
        fileReader.onload = (_event: Event ) =&gt; {
          const base64Img = (fileReader.result as string).substring((fileReader.result as string).indexOf( ',' ) + 1 );
          if (this.form.controls.picture.value !== base64Img) {
            this.form.controls.picture.setValue( base64Img );
            this.form.controls.uploadPicture.setValue( true );
            this.cd.markForCheck();
          }
        };
  
        fileReader.readAsDataURL( fileInput.files[ 0 ] );
        this.cd.markForCheck();
      } else {
        this.form.controls.picture.setValue( this.profile.picture );
        this.form.controls.uploadPicture.setValue( false );
      }
    }
  
  But nothing changed
  </code></pre>