ngFor задерживает отображение до тех пор, пока следующий элемент не будет помещен в массив

#javascript #html #angular #frontend

#javascript #HTML #угловой #интерфейс

Вопрос:

У меня есть компонент uploader, который работает как dropzone для файлов. Когда я отбрасываю файлы туда, они собираются files: File[] . Я хочу отобразить их с помощью ngFor, чтобы пользователь знал, что загружается. Проблема в том, что когда я загружаю файл, буфер преобразует его в urls: [] . Я проверил это в отладчике, и значения URL-адресов действительно были там. Но * ngFor не отображает их, пока я не наведу курсор на следующее изображение в dropzone (просто наведите курсор, без удаления). Сценарий выглядит так: я загружаю изображение, и ngFor не отображает изображения. Я навожу второе изображение на dropzone, и отображается первое изображение. Я отбрасываю второе изображение, а затем все еще отображается первое изображение. Затем я навожу другой и показываю второй.

Это мой код компонента:

 import { Component, OnInit, Input, OnDestroy, Output, EventEmitter } from '@angular/core';
import { Observable, Subscription } from 'rxjs';
import { StorageService } from 'src/app/core/services/storage.service';
import { AngularFireUploadTask } from '@angular/fire/storage';
import { ProductService } from 'src/app/core/services/product.service';

@Component({
  selector: 'app-uploader',
  templateUrl: './uploader.component.html',
  styleUrls: ['./uploader.component.scss']
})
export class UploaderComponent implements OnInit, OnDestroy {

  eventSubscriptions: Subscription[] = [];
  @Input() productIdEvent: Observable<void>;
  @Input() resetEvent: Observable<void>;
  @Output() onChange = new EventEmitter<number>();

  isHovering: boolean;

  files: File[] = [];
  urls = [];

  task: AngularFireUploadTask;

  constructor(private storageService: StorageService, private productService: ProductService) { }
  
  toggleHover(event: boolean) {
    this.isHovering = event;
  }
  onDrop(files: FileList) {
    for (let i = 0; i < files.length; i  ) {
      this.files.push(files.item(i));
      var reader = new FileReader();

      reader.readAsDataURL(files.item(i)); // read file as data url

      reader.onload = (event) => { // called once readAsDataURL is completed=
        this.urls.push(reader.result);
      }
    }
  }

  uploadFiles(productId){
    let id = 0;
    for(let file of this.files){
      this.storageService.uploadFile(file,productId   "/" (  id))
      let reader = new FileReader();
    }
  }

  ngOnInit(): void {
    this.eventSubscriptions.push(this.productIdEvent.subscribe(val => this.uploadFiles(val)));
    this.eventSubscriptions.push(this.resetEvent.subscribe(() => this.files = []));
  }

  ngOnDestroy(): void {
    this.eventSubscriptions.forEach(subscription => subscription.unsubscribe());
  }
}
  

И html

 <div class="dropzone" dropzone (hovered)="toggleHover($event)" (dropped)="onDrop($event)" [class.hovering]="isHovering">
    <h3>Strefa zrzutu</h3>
</div>
<div><img *ngFor="let url of urls" [src]="url"></div>
<div>{{files.length}} zdjęcia</div>
  

директива

 import { Directive, Output, EventEmitter, HostListener } from '@angular/core';

@Directive({
  selector: '[dropzone]'
})
export class DropzoneDirective {

  @Output() dropped = new EventEmitter<FileList>();
  @Output() hovered = new EventEmitter<boolean>();

  @HostListener('drop', ['$event'])
  onDrop($event) {
    $event.preventDefault();
    this.dropped.emit($event.dataTransfer.files);
    this.hovered.emit(false);
  }

  @HostListener('dragover', ['$event'])
  onDragOver($event) {
    $event.preventDefault();
    this.hovered.emit(true);
  }

  @HostListener('dragleave', ['$event'])
  onDragLeave($event) {
    $event.preventDefault();
    this.hovered.emit(false);
  }

}
  

Комментарии:

1. похоже на ошибку с вашим обнаружением изменений. возможно, вызвано dropzone директивой или какой-либо другой библиотекой, предоставляющей это. вероятно, это связано с ngZone соображениями производительности и не осознает последствий этого.

2. @bryan60 обновлено

3. хм .. выглядит нормально. используете ли вы OnPush обнаружение изменений где-то выше в вашем дереве компонентов? Если нет, вы можете захотеть воссоздать это в stackblitz (без базы данных для хранения / загрузки / запуска)

4. да, dropzone является частью формы. Я удалил OnPush из родительского компонента, но это не помогло

5. для меня все это выглядит достаточно хорошо, если вы не создаете onpush-часть дерева. не ясно, что вы пытаетесь сделать внутри своей uploadFiles функции, где вы создаете экземпляр FileReader , а затем ничего с ним не делаете, но idk, что вы ожидаете, что там произойдет. я бы попытался воссоздать это в stack blitz.