简体   繁体   English

Angular Reactive Form 编辑模式,如何重新填充输入类型=文件?

[英]Angular Reactive Form Edit Mode, How to RePopulate Input type=file?

How can I repopulate my file with input type=file within a reactive form?如何在反应形式中使用 input type=file 重新填充我的文件?

I am working on a project where I have a form to submit data (workouts) which builds out a list according to the form.我正在做一个项目,我有一个表单来提交数据(锻炼),它根据表单构建一个列表。 From there, you can go in to view the workout submitted, and edit it.从那里,您可以进入查看提交的锻炼并对其进行编辑。

When you click "Edit Workout", the same submission form is opened up, this time populated with all the data.当您单击“编辑锻炼”时,会打开相同的提交表单,这次填充了所有数据。 This is done by linking the "Edit Workout" to the EditComponent , and then ngOnInit checks the route params.这是通过将“Edit Workout”链接到EditComponentEditComponent ,然后ngOnInit检查路由参数。 If the route params have an id , then it initializes the form with the workout data.如果路线参数有一个id ,那么它会用锻炼数据初始化表单。

This is all working just fine.这一切都很好。

The problem I am having relates specifically to the repopulation of the <input type="file"...> .我遇到的问题特别与<input type="file"...>的重新填充有关。

Here is my Form Initialization when I go to edit First, I set all variables blank on initForm() , then if the editMode is true (if I am editing), then it will fill each of those empty variables with the data coming from the server (or dummy data).这是我进行编辑时的表单初始化首先,我将initForm()上的所有变量设置为空白,然后如果editMode为真(如果我正在编辑),那么它将用来自服务器(或虚拟数据)。


private initForm() {

// set all values to empty
  let workoutImageUrl = "";
  let workoutTitle = "";
  let workoutPhase = [];
  let workoutType = "";
  let workoutDuration = "";
  let workoutSpecialty = [];
  let workoutDescription = "";
  let workoutZwo = "";

// check if in edit mode, and if so, make values equal to database data
 if (this.editMode) {
    const workout = this.workoutService.getWorkout(this.id);
    workoutTitle = workout.title;
    workoutImageUrl = workout.imageUrl;
    workoutPhase = workout.phase;
    workoutType = workout.type;
    workoutDuration = workout.duration;
    workoutSpecialty = workout.specialty;
    workoutZwo = workout.zwo;
    workoutDescription = workout.description;
   this.imgSrc = workout.imageUrl;
    console.log("workout that should be here: ", workout);
  }

// Populate form either with empty data or editable data
 this.workoutForm = new FormGroup({
    title: new FormControl(workoutTitle, Validators.required),
    imageUrl: new FormControl(workoutImageUrl),
    phase: new FormControl(workoutPhase, Validators.required),
    duration: new FormControl(workoutDuration, Validators.required),
    type: new FormControl(workoutType, Validators.required),
    specialty: new FormControl(workoutSpecialty, Validators.required),
    zwo: new FormControl(workoutZwo, Validators.required),
    description: new FormControl(workoutDescription, Validators.required)
  });
}

When I do this for a new workout (standard form), there is no problem.当我为新的锻炼(标准形式)执行此操作时,没有问题。 The problem comes when I am in EditMode and trying to repopulate data within the <input type="file"...> like mentioned above.当我处于 EditMode 并尝试在<input type="file"...>重新填充数据时,问题就出现了,如上所述。

Right now, my workoutImageUrl or workout.imageUrl is coming from a Url given from my FireBase Storage so I can see why there is an issue here with the input type.现在,我的workoutImageUrlworkout.imageUrl Url 来自我的FireBase 存储提供的Url,所以我可以理解为什么这里输入类型存在问题。 The same happens if I use a local file, it cannot be re-populated to the input type of file.如果我使用本地文件,也会发生同样的情况,它无法重新填充到文件的输入类型。

I imagine this is due to security issues, but this is causing my form to get the following error: from Chrome: "ERROR DOMException: Failed to set the 'value' property on 'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string."我想这是由于安全问题,但这导致我的表单出现以下错误:来自 Chrome:“错误 DOMException:无法在 'HTMLInputElement' 上设置 'value' 属性:此输入元素接受一个文件名,这可能只能以编程方式设置为空字符串。” from FF: "ERROR DOMException: "An attempt was made to use an object that is not, or is no longer, usable"来自 FF:“错误 DOMException:”尝试使用不可用或不再可用的对象”

Is there any way I can "repopulate" my image for when I am editing a workout?当我编辑锻炼时,有什么方法可以“重新填充”我的图像? So the file appears as the current item within the workout?所以文件显示为锻炼中的当前项目?

Like mentioned above, I have tried using local image files which gives the same result.如上所述,我尝试使用本地图像文件,但结果相同。 I have also tried removing the imageUrl from the form group completely, but this doesn't help as I want it to be in the form group.我还尝试从表单组中完全删除 imageUrl,但这无济于事,因为我希望它位于表单组中。 I have also tried changing the workoutImageUrl to be the same as the imgSrc coming from Firebase.我还尝试将workoutImageUrl更改为与来自 Firebase 的imgSrc相同。 This works if I want to show the current workout image, but it does not work when I submit because I get an error that the current image is null.如果我想显示当前的锻炼图像,这可以工作,但是当我提交时它不起作用,因为我收到当前图像为空的错误。

Here is a StackBlitz for this project (a little bit simplified).这是这个项目的 StackBlitz (稍微简化了一点)。 If you click on a workout in the list, you can then select "manage workout" and "edit workout" to get to the edit page with the filled form (currently not working with the image upload).如果您单击列表中的锻炼,则可以选择“管理锻炼”和“编辑锻炼”以进入带有填写表格的编辑页面(目前无法使用图像上传)。

The WorkoutEditComponent can be found through src / app / features / workouts-page / workout-edit . WorkoutEditComponent可以通过src / app / features / workouts-page / workout-edit WorkoutEditComponent src / app / features / workouts-page / workout-edit You can see in here the logic of the image upload, but I don't think this matters to this specific issue.您可以在此处看到图像上传的逻辑,但我认为这与此特定问题无关。 The database storage is not working on the StackBlitz, but the uploading of the images is working (it just isn't saving).数据库存储在 StackBlitz 上不起作用,但图像的上传正在起作用(它只是不保存)。 I put in a sample API to get this working minimally for this StackBlitz.我放入了一个示例 API,以使其在此 StackBlitz 中最低限度地工作。

The WorkoutsListComponent is where the list of workouts submitted is being displayed. WorkoutsListComponent是显示提交的锻炼列表的地方。

Is there any way to retain this workout image file when I view it while editing a workout?当我在编辑锻炼时查看它时,有什么方法可以保留此锻炼图像文件? And at the same time, not have to re-upload the image while editing a form?同时,编辑表单时不必重新上传图像? I had the image set to display in the bottom of the edit page, but if it displays here, it still does not re-upload or use the same image from the server.我将图像设置为显示在编辑页面的底部,但如果它显示在此处,它仍然不会重新上传或使用来自服务器的相同图像。 I would have to re-upload the image every time.我每次都必须重新上传图像。

Please let me know if there is anything I can explain or expound on.请让我知道是否有任何我可以解释或阐述的内容。

What you need is re-creating the Object file from image url then assign it to input file.您需要的是从图像 url 重新创建对象文件,然后将其分配给输入文件。

I came up with this idea :我想出了这个主意:

@ViewChild('fileInput', { static : false}) fileInput : ElementRef; //declaration

private initForm() {
    let workoutImageUrl = "";
    let workoutTitle = "";
    let workoutPhase = [];
    let workoutType = "";
    let workoutDuration = "";
    let workoutSpecialty = [];
    let workoutDescription = "";
    let workoutZwo = "";

    if (this.editMode) {
      const workout = this.workoutService.getWorkout(this.id);
      workoutTitle = workout.title;
      workoutImageUrl = workout.imageUrl;

      workoutPhase = workout.phase;
      workoutType = workout.type;
      workoutDuration = workout.duration;
      workoutSpecialty = workout.specialty;
      workoutZwo = workout.zwo;
      workoutDescription = workout.description;
    }

    this.workoutForm = new FormGroup({
      title: new FormControl(workoutTitle, Validators.required),
      imageUrl: new FormControl(''),
      phase: new FormControl(workoutPhase, Validators.required),
      duration: new FormControl(workoutDuration, Validators.required),
      type: new FormControl(workoutType, Validators.required),
      specialty: new FormControl(workoutSpecialty, Validators.required),
      zwo: new FormControl(workoutZwo, Validators.required),
      description: new FormControl(workoutDescription, Validators.required)
    });
    let image_name = workoutImageUrl.split("/").pop();
    image_name = image_name.split('?')[0];
    fetch(workoutImageUrl, { mode: 'no-cors'})
      .then(res => res.blob())
      .then(blob => {
        const data = new ClipboardEvent('').clipboardData || new DataTransfer();
        data.items.add(new File([blob], image_name));
        this.fileInput.nativeElement.files = data.files;
        this.fileInput.nativeElement.value = data.files[0];
    });
}

PS : I have try it on your stackblitz Example and it's working i hope this helps. PS:我已经在你的 stackblitz Example 上试过了,它正在工作,我希望这会有所帮助。 Good luck!祝你好运!

Another way to fix cors problem is to use this :解决 cors 问题的另一种方法是使用这个:

const proxyUrl = 'https://cors-anywhere.herokuapp.com/';
fetch(proxyUrl  + workoutImageUrl)

I suggest create a function that return a FormGroup我建议创建一个返回 FormGroup 的函数

initForm(data:any)
{
  data=data || {
     workoutImageUrl:'';
     workoutTitle:"";
     workoutPhase:[];
     workoutType:"";
     workoutDuration:"";
     workoutSpecialty:[];
     workoutDescription:"";
     workoutZwo:"";
  return new FormGroup({
      title: new FormControl(data.workoutTitle, Validators.required),
      imageUrl: new FormControl(''),
      phase: new FormControl(data.workoutPhase, Validators.required),
      duration: new FormControl(data.workoutDuration, Validators.required),
      type: new FormControl(data.workoutType, Validators.required),
      specialty: new FormControl(data.workoutSpecialty, Validators.required),
      zwo: new FormControl(data.workoutZwo, Validators.required),
      description: new FormControl(data.workoutDescription, Validators.required)
    });
}

you use你用

this.form=this.initForm(null) //<--an empty form
this.form=this.initForm(workout ) //<--a form with the data

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM