Angular 8-在前端更改检测问题上调整图像大小

我正在尝试从网上找到的一些教程中构建图像压缩器服务。 服务 本身可以正常工作,接收图像作为File,然后对其进行压缩并返回 Observable 。 除了将压缩的图片上传到服务器之前,我希望在组件中使用压缩的图片,一切都很好。

该组件无法检测到何时通过 async 管道到达了新的压缩图像。如果我手动订阅了Observable,则会按预期方式获得图像数据,但是如果尝试使用它更新组件属性,它不会立即更改视图,而是如果我使用旧的“图像数据”来更改它尝试压缩新图像。

我发现,如果部分代码在ngZone之外解析,则可能会出现此问题,因此我发现了一种解决方法(请参见代码下面的内容),注入 ApplicationRef 和使用 .tick() 实际上效果很好,但使我的服务难以重用。

我的问题是: 服务代码的哪一部分运行在ngZone之外,还有哪些可能的修复程序或解决方法,因此该服务可在其他组件中重用,而不必每次服务发出数据时都注入ApplicationRef和.tick()。

这是我的 服务代码

 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<ICompressedImage>)  {
 return (progressEvent : ProgressEvent) => {
  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<ICompressedImage>) {
  return () => {
  const canvas = document.createElement('canvas');
  canvas.width = 100;
  canvas.height = 100;
  const context = <CanvasRenderingContext2D>canvas.getcontext('2d');
  context.drawImage(image,100,100);
  this.toICompressedImage(context,observer);
}}

// Emit CompressedImage
toICompressedImage(context : CanvasRenderingContext2D,observer : Subscriber<ICompressedImage> ) {
  context.canvas.toBlob(
    (blob) => {
      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<ICompressedImage> {
   this._currentFile = file ;
   return new Observable<ICompressedImage>(
     observer => {
       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 ;
}

这是我的 component.ts

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>0) {
    this.selectedImage = fl.item(0);
    this.compressor
    .compress(this.selectedImage)
    .subscribe(data => {
     this.compressedImage = data ;
     this.ar.tick();
    });
  } else {
    console.error('No file/s selected');

  }
  }


}

这是我的 HTML模板

<div style='border : 1px solid green;'>
    <input type='file' #SelectedFile (change)="compress($event.target.files)" accept='image/*' >
</div>


<div
style = 'border : 1px solid blue ; height : 200px;'
*ngIf="compressedImage " >
 <strong>File Name : </strong>{{ compressedImage?.name }}

<img *ngIf="compressedImage?.imgUrl as src"
[src]= 'src' >
</div>

我显示代码的方式非常完美。尝试注释掉   component.ts 压缩方法 中的 this.ar.tick(); >文件并查看更改。

fandongdong888 回答:Angular 8-在前端更改检测问题上调整图像大小

经过几个小时的挖掘,我找到了一个可行的解决方案。我已经在服务中注入了NgZone包装器。之后,在我的compress方法中,我使用zone.runOutsideAngular()运行所有文件处理代码,从而防止了故意的ChangeDetection,并且一旦完成大小调整操作并且新的压缩映像可用,我便在运行下一个方法带zone.Run()的观察者(订阅者),它实际上在Angular的区域内运行代码,强制执行ChangeDetection。 我已经测试了在组件中手动订阅生成的observable以及通过异步管道订阅的情况。两者都像魅力。使用异步管道发布代码。

service.ts:

import { Observable,Subscriber } from 'rxjs';
import { Injectable,NgZone } 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,private _zone : NgZone) {

}

// FileReader Onload callback
readerOnload(observer : Subscriber<ICompressedImage>)  {
 return (progressEvent : ProgressEvent) => {
  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<ICompressedImage>) {
  return () => {
  const canvas = document.createElement('canvas');
  canvas.width = 100;
  canvas.height = 100;
  const context = <CanvasRenderingContext2D>canvas.getContext('2d');
  context.drawImage(image,100,100);
  this.toICompressedImage(context,observer);
}}

// Emit CompressedImage
toICompressedImage(context : CanvasRenderingContext2D,observer : Subscriber<ICompressedImage> ) {
  context.canvas.toBlob(
    (blob) => {
      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 ;
      this._zone.run(() => {
        observer.next(this._currentImage);
        observer.complete();
      })

    },'image/jpeg',1
  );
}

//  Compress function
 compress(file : File) : Observable<ICompressedImage> {
   this._currentFile = file ;
   return new Observable<ICompressedImage>(
     observer => {
       this._zone.runOutsideAngular(() => {
        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 ;
}

component.ts:

import { Component,OnInit } from '@angular/core';
import { ImageCompressorService,ICompressedImage } from 'src/app/shared/services/image-compressor.service';
import { Observable } from 'rxjs';


@Component({
  selector: 'app-new-project',templateUrl: './new-project.component.html',styleUrls: ['./new-project.component.css']
})
export class NewProjectComponent implements OnInit  {
// globals
private selectedImage ;
compressedImage : Observable<ICompressedImage>;

// Constructor
  constructor( private compressor : ImageCompressorService) {}

// OnInit implementation
     ngOnInit(): void {}

// Compress method
  compress(fl : FileList) {
if (fl.length>0) {
    this.selectedImage = fl.item(0);
  this.compressedImage =  this.compressor.compress(this.selectedImage)
  } else {

    console.error('No file/s selected');

  }
  }
}

component.html:

<div style='border : 1px solid green;'>
    <input type='file' #SelectedFile (change)="compress($event.target.files)" accept='image/*' >
</div>


<div
style = 'border : 1px solid blue ; height : 200px;'
*ngIf="compressedImage | async as image" >
 <strong>File Name : </strong>{{ image.name }}

<img *ngIf="image.imgUrl as src"
[src]= 'src' >
</div>
本文链接:https://www.f2er.com/3033552.html

大家都在问