【问题标题】:Angular Reactive Form Edit Mode, How to RePopulate Input type=file?Angular 反应式表单编辑模式,如何重新填充输入类型 = 文件?
【发布时间】:2020-06-06 08:13:36
【问题描述】:

如何在响应式表单中使用 input type=file 重新填充我的文件?

我正在做一个项目,我有一个表单来提交数据(锻炼),该表单根据表单构建了一个列表。从那里,您可以进入查看提交的锻炼,并对其进行编辑。

当您单击“编辑锻炼”时,会打开相同的提交表单,这一次会填充所有数据。这是通过将“编辑锻炼”链接到 EditComponent 来完成的,然后 ngOnInit 检查路线参数。如果路线参数有id,那么它会使用锻炼数据初始化表单。

这一切都很好。

我遇到的问题特别与<input type="file"...> 的重新填充有关。

这是我去编辑时的表单初始化 首先,我将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)
  });
}

当我为新的锻炼(标准形式)执行此操作时,没有问题。当我处于 EditMode 并尝试在 <input type="file"...> 中重新填充数据时,问题就出现了,如上所述。

现在,我的 workoutImageUrlworkout.imageUrl 来自我的 FireBase 存储提供的 Url,因此我可以看到输入类型存在问题的原因。如果我使用本地文件也会发生同样的情况,它不能重新填充到文件的输入类型。

我想这是由于安全问题,但这导致我的表单出现以下错误: 来自 Chrome:“错误 DOMException:无法在 'HTMLInputElement' 上设置 'value' 属性:此输入元素接受一个文件名,该文件名只能以编程方式设置为空字符串。” 来自 FF: "ERROR DOMException: "试图使用一个不可用或不再可用的对象"

当我编辑锻炼时,有什么方法可以“重新填充”我的图像?所以文件显示为锻炼中的当前项目?

如上所述,我尝试使用本地图像文件,结果相同。我也尝试过从表单组中完全删除 imageUrl,但这并没有帮助,因为我 希望 它位于表单组中。我还尝试将 workoutImageUrl 更改为与来自 Firebase 的 imgSrc 相同。如果我想显示当前的锻炼图像,这可以工作,但是当我提交时它不起作用,因为我收到当前图像为空的错误。

Here is a StackBlitz for this project(有点简化)。如果您单击列表中的锻炼,则可以选择“管理锻炼”和“编辑锻炼”以进入已填写表格的编辑页面(目前不适用于图片上传)。

WorkoutEditComponent 可以通过src / app / features / workouts-page / workout-edit 找到。你可以在这里看到图片上传的逻辑,但我认为这对这个特定问题并不重要。 StackBlitz 上的数据库存储不工作,但图像的上传工作正常(只是没有保存)。我放入了一个示例 API,以使这个 StackBlitz 的工作最小化。

WorkoutsListComponent 是显示提交的锻炼列表的位置。

我在编辑锻炼时查看此锻炼图像文件时,有什么方法可以保留它?同时,在编辑表格时不必重新上传图像?我将图像设置为显示在编辑页面的底部,但如果它显示在这里,它仍然不会重新上传或使用来自服务器的相同图像。我每次都必须重新上传图片。

如果有什么我可以解释或阐述的,请告诉我。

【问题讨论】:

    标签: javascript html angular typescript firebase


    【解决方案1】:

    您需要从图像 url 重新创建 Object 文件,然后将其分配给输入文件。

    我想出了这个主意:

    @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:我已经在您的 stackblitz Example 上进行了尝试,它正在工作,我希望这会有所帮助。祝你好运!

    解决 cors 问题的另一种方法是使用这个:

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

    【讨论】:

    • 您好,这对于来自本地文件的图像非常有效,但是对于托管在网络上的图像呢?例如,我已更新此 stackblitz 中的图像文件以显示来自 firebase 存储的图像,但代码无法在查看模式下将这些图像保留在输入中。再次感谢stackblitz.com/edit/github-x1tx9w-g3xjfx
    • 这是一个 cors 策略问题。我已经更新了我的答案来修复它。
    • 我为另一个问题道歉。该 url 以冒号 : 出现,而​​不是 url 上的斜杠 /。这使它无效。有没有解决的办法?我没有看到任何东西。
    • 是的,这很正常,因为锻炼图像Url有来自firebase存储的url,我们直接使用它来创建文件对象
    • 因为冒号,文件输入返回为null,可惜这个不起作用
    【解决方案2】:

    我建议创建一个返回 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)
        });
    }
    

    你使用

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

    【讨论】:

      猜你喜欢
      • 2021-12-19
      • 1970-01-01
      • 2017-06-05
      • 2017-12-09
      • 2016-01-22
      • 1970-01-01
      • 2018-07-22
      • 2019-05-24
      • 2012-11-14
      相关资源
      最近更新 更多