Angular Функция вызывается дважды и FileReader вызывает бесконечный цикл - PullRequest
0 голосов
/ 16 марта 2020

Я делаю простой код, чтобы принять несколько значений в одной форме и отобразить список значений формы с правой стороны. Я создал модальный класс полей формы и при отправке отправляю данные в Сервис. В другом компоненте я подписался на данные. По какой-то неизвестной причине мои функции во втором компоненте называются дважды. Также я принимаю файл изображения или URL изображения в форме и генерирую предварительный просмотр файла, выбранного в форме, а также в списке. Чтобы создать предварительный просмотр этого файла в форме, он работает безупречно, но во втором компоненте тот же код переходит в бесконечное число l oop. Любой

мой основной компонент формы html

<div class="row form">
  <div class="col-8">
    <form [formGroup]="songMetadata" (submit)="onSubmit(songMetadata)">
      <div class="row">
        <div class="col form-data">
          <mat-form-field>
            <mat-label>Song Name</mat-label>
            <input matInput formControlName="songName" />
          </mat-form-field>
          <br />
          <br />
          <mat-form-field>
            <mat-label>Artist Name</mat-label>
            <input matInput formControlName="artistName" />
          </mat-form-field>
          <br />
          <br />
          <mat-form-field>
            <mat-label>Album Name</mat-label>
            <input matInput formControlName="albumName" />
          </mat-form-field>
          <br />
          <br />
          <mat-form-field>
            <mat-label>Spotify URL</mat-label>
            <input matInput formControlName="url" />
          </mat-form-field>
          <br />
          <br />
          <mat-form-field>
            <mat-label>Other Description</mat-label>
            <textarea
              matInput
              formControlName="description"
              rows="4"
            ></textarea>
          </mat-form-field>
          <br />
          <br />
          <button
            type="submit"
            class="btn btn-primary"
            [disabled]="
              songMetadata.invalid ||
              (!isImageFileSelected && !isImageURLEntered)
            "
          >
            Submit
          </button>
        </div>
        <div class="col image-upload">
          <div
            dropZone
            class="text-center dropzone"
            (hovered)="changeIsHover($event)"
            (dropped)="fileDropped($event)"
            [class.hovering]="isHovering"
          >
            <img
              *ngIf="isImageFileSelected || isImageURLEntered"
              [src]="imagePreview"
              alt=""
              width="192"
              height="190"
            />
            <div
              class="drop-text"
              *ngIf="!isImageFileSelected && !isImageURLEntered"
            >
              Drag And Drop File Here
            </div>
          </div>
          <input
            type="text"
            placeHolder="Or Enter URL"
            (blur)="loadPreviewFromURL($event)"
          />
          <br />
          <label for="files" class="btn btn-primary">Or Select Image</label>
          <br />
          <input
            id="files"
            style="visibility:hidden;"
            type="file"
            (change)="fileSelected($event)"
          />
          <br />
          <!-- <input type="file" /> -->
        </div>
      </div>
    </form>
  </div>
  <!-- List View -->
  <div class="col-4">
    <list></list>
  </div>
</div>

Основной компонент TS.

import { DataService } from "./data.service";
import { AngularFireStorageModule } from "@angular/fire/storage";
import { Component, OnInit } from "@angular/core";
import { FormGroup, FormBuilder, Validators } from "@angular/forms";
import { Observable } from "rxjs";
import { ISong } from "./song";
import { Data } from "@angular/router";

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.scss"]
})
export class AppComponent implements OnInit {
  songMetadata: FormGroup;
  selectedFile: File = null;
  isImageFileSelected: boolean;
  isImageURLEntered: boolean;
  isHovering: boolean;
  song: ISong;
  imagePreview: any;
  // // Upload Related Stuff
  // task: AngularFireUploadTask;
  // snapshot: Observable<any>;

  constructor(
    private formBuilder: FormBuilder,
    private service: DataService // private storage: AngularFireStorage
  ) {
    this.isImageFileSelected = false;
    this.isHovering = false;
    this.isImageURLEntered = false;
  }

  ngOnInit() {
    this.songMetadata = this.formBuilder.group({
      songName: ["xzcvzxv", Validators.required],
      artistName: ["xzcvcxzv", Validators.required],
      albumName: ["zxcvcxv", Validators.required],
      url: ["zxcvvc", Validators.required],
      description: ["zxcvzcxv", Validators.required]
    });
  }

  fileSelected(event: any) {
    this.isImageFileSelected = true;
    this.isImageURLEntered = false;
    this.selectedFile = event.target.files[0];
    this.loadPreview();
  }

  fileDropped(event: FileList) {
    this.isImageFileSelected = true;
    this.isImageURLEntered = false;
    this.selectedFile = event.item(0);
    this.loadPreview();
  }

  onSubmit(songForm: FormGroup) {
    event.preventDefault();
    if (this.isImageFileSelected) {
      this.song = {
        name: songForm.value.songName,
        artist: songForm.value.artistName,
        album: songForm.value.albumName,
        url: songForm.value.url,
        description: songForm.value.description,
        imageFile: this.selectedFile,
        imageURL: null
      };
    } else {
      this.song = {
        name: songForm.value.songName,
        artist: songForm.value.artistName,
        album: songForm.value.albumName,
        url: songForm.value.url,
        description: songForm.value.description,
        imageFile: null,
        imageURL: this.imagePreview
      };
    }
    this.service.addSong(this.song);
  }

  changeIsHover(isHovering: boolean) {
    this.isHovering = isHovering;
  }

  loadPreview = () => {
    let reader = new FileReader();
    reader.readAsDataURL(this.selectedFile);
    reader.onload = event => {
      this.imagePreview = reader.result;
    };
    console.log("PREVIEW HIT");
  };

  loadPreviewFromURL(event: any) {
    console.log(event.target.value);
    this.isImageURLEntered = true;
    this.isImageFileSelected = false;
    this.imagePreview = event.target.value;
  }
}

Компонент списка HTML

<div *ngFor="let s of songs; let i = index">
  <div class="col-4">
    <img
      *ngIf="s?.imageURL"
      [src]="livePrvw(s?.imageURL)"
      width="100"
      height="100"
      alt="Image URL Preview"
    />
    <img
      *ngIf="s?.imageFile"
      [src]="livePreview(s?.imageFile)"
      width="100"
      height="100"
      alt="Image FILE Preview"
    />
  </div>
  <div class="col-8">
    {{ s.name }}
  </div>
</div>

listComponent TS

import { DataService } from "./../data.service";
import { ISong } from "./../song";
import { Component, OnInit, OnChanges } from "@angular/core";

@Component({
  selector: "list",
  templateUrl: "./list.component.html",
  styleUrls: ["./list.component.scss"]
})
export class ListComponent implements OnInit {
  songs: ISong[];
  // song: ISong;

  constructor(private service: DataService) {}

  ngOnInit() {
    this.service.songsAsObservable.subscribe(data => {
      this.songs = data;
    });
  }

  //  also getting called twice
  livePreview(file: File) {
    // Going into infinite loop

    // let newReader = new FileReader();
    // let imagePreview: any;
    // newReader.readAsDataURL(file);

    // newReader.onload = event => {
    //   imagePreview = newReader.result;
    // };

    console.log(file);
  }

  // getting called twice somehow
  livePrvw(imageURL: string) {
    console.log(imageURL);
    return imageURL;
  }
}

Повторять методы в ListComponent Get Called Twice без причины и работать с FileReader Code в главном компоненте не работает в ListComponent (идет в бесконечность L oop)

Любая помощь очень ценится

1 Ответ

0 голосов
/ 16 марта 2020

Ваша проблема

У вас есть форма, которая позволяет пользователям загружать изображение или указывать URL-адрес изображения.

Когда форма отправлена, вы отображаете загруженное изображение.

Функции, возвращающие URL-адрес изображения, запускаются несколько раз или не работают.

Мой диагноз

Есть несколько проблем с вашим текущим решением.

  1. Многократное выполнение функции

Вы связываете <img src /> с функцией, которая разрешает URL. Каждый раз, когда обнаруживается изменение, ваша функция будет запрашиваться. Вы должны выполнять как можно меньше обработки в цикле обнаружения изменений.

Обычно это можно исправить, установив для компонента changeDetectionStrategy значение OnPush, добавив ChangeDetectorRef и вручную вызвав detectChanges() когда вы хотите активировать обнаружение изменений. Тем не менее, я думаю, что в этом случае это будет покрывать трещины, так что это не мое предлагаемое решение.

Невозможно разрешить URL для загруженного файла

Вы пытаетесь привязать <img src /> к функции, которая получает URL данных через FileReader. Считыватель файлов выполняется асинхронно, поэтому вам нужно будет обернуть это в какое-то обещание / наблюдаемое и использовать для вызова этот канал async.

НО вам не следует делать это по причине, указанной в пункт 1.

Мой подход

Я собираюсь сосредоточиться на конкретной c проблеме разрешения URL-адреса изображения, поскольку у вас есть сложная форма, которая в основном выходит за рамки для этой проблемы.

Мой главный рефакторинг - вывести разрешение URL из цикла обнаружения изменений. Сначала я разрешу URL, а затем добавлю элемент в массив с разрешенным URL.

Моя реализация

Я собираюсь абстрагировать вашу проблему в простое приложение, в которое вы можете загрузить изображение и / или указать URL изображения. Разрешенные изображения будут отображаться после отправки формы.

Компонент довольно тривиален, поэтому я покажу службу только здесь:

image.service.ts

export class ImageService {
  private images$ = new BehaviorSubject<{ url: string, type: string }[]>([]);
  private images: { url: string, type: string }[] = [];

  addFromFile(file: File) {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = event => {
      this.addImage({
        url: reader.result.toString(),
        type: 'fromFile'
      });
    };    
  }

  addFromUrl(url: string) {
    this.addImage({
      url: url,
      type: 'fromUrl'
    });
  }

  getImages(): Observable<{ url: string, type: string }[]> {
    return this.images$.asObservable();
  }

  private addImage(image: { url: string, type: string }) {
    this.images.push(image);
    this.images$.next(this.images);
  }
}

Обратите внимание, что изображения помещаются в массив и тему только после разрешения URL.

Я определенно упростил это по сравнению с вашей формой, но я думаю, что вы могли бы черпать вдохновение из этот подход, чтобы исправить вашу проблему.

DEMO: https://stackblitz.com/edit/angular-yhnnwv

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...