我正在编写一个简单的代码,以在一个Form中接受一些值,并在右侧显示表单值的列表。我已经创建了表单字段的模态类,并且在提交时,我正在将数据发送到服务。在其他组件中,我已订阅数据。由于某些未知的原因,第二个组件中的My函数被称为Twice。另外,我接受表单中的图像文件或图像URL,并生成在表单中以及列表中选择的文件的预览。要在Form中生成此文件的预览,它可以正常工作,但在Second组件中,相同的代码将进入无限循环。任何
我的主要表单组件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中的方法两次,并且在Main Component中工作的FileReader代码在ListComponent中不起作用(进入无限循环)
StackBlitzhttps://stackblitz.com/github/Luciferdev7796/naada/tree/master
非常感谢任何帮助
您具有一个允许用户上载图像或指定图像URL的表单。
提交表单后,您将渲染上载的图像。
返回图像URL的函数多次运行或不起作用。
您当前的解决方案有一些问题。
您正在将<img src />
绑定到解析URL的函数。每次运行更改检测时,都会查询您的功能。您应该在变更检测周期中尽可能少地进行处理。
通常,通过将组件changeDetectionStrategy
设置为OnPush
,注入ChangeDetectorRef
并在您要触发更改检测时手动调用detectChanges()
,可以解决此问题。但是,在这种情况下,我认为这可以解决裂缝,因此不是我建议的解决方案。
您正在尝试将<img src />
绑定到通过FileReader
获取数据URL的函数。文件读取器异步执行,因此您需要将其包装在某种Promise / Observable中,然后使用async
管道进行调用。
但是出于第1点所述的原因,您不应该这样做。
我将着重解决如何解析图像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后,如何仅将图像推送到数组和主题中。
与您的表格相比,我绝对简化了很多,但是我认为您可以从这种方法中得到一些启发来解决您的问题。