角反应式表单编辑模式,如何重新填充输入type = file?

时间:2020-02-21 21:28:37

标签: javascript html angular typescript firebase

如何用反应形式以输入type = file填充文件?

我正在一个项目中,我有一个表单来提交数据(锻炼),该表单根据表单建立了一个列表。在这里,您可以查看提交的锻炼并进行编辑。

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

一切正常。

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

这是我进行编辑时的表单初始化 首先,我将initForm()上的所有变量都设置为空白,然后如果editMode为true(如果我 am 编辑),则它将用数据填充这些空变量中的每一个来自服务器(或伪数据)。


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存储指定的网址,因此我可以看到为什么输入类型存在问题。如果我使用本地文件,也会发生同样的情况,无法将其重新填充为文件的输入类型。

我想这是由于安全问题引起的,但是这导致我的表单出现以下错误: 从Chrome:“ ERROR 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是显示提交的锻炼列表的地方。

在锻炼时查看该锻炼图像文件时,有什么办法?同时,在编辑表单时不必重新上传图像吗?我将图像设置为显示在编辑页面的底部,但是如果显示在此处,它仍然不会重新上传或使用服务器上的同一图像。我每次都必须重新上传图片。

请告诉我是否有什么我可以解释或解释的东西。

2 个答案:

答案 0 :(得分:1)

您需要的是从图像URL重新创建目标文件,然后将其分配给输入文件。

我想到了这个主意:

@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示例中进行了尝试,它正在运行,希望对您有所帮助。祝你好运!

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

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

答案 1 :(得分:0)

我建议创建一个返回FormGroup的函数

CREATE TABLE users
( user_id int(5) NOT NULL,
  username varchar(25) NOT NULL,
  password varchar(30) NOT NULL,
  PRIMARY KEY(user_id)
  );

您使用

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)
    });
}
相关问题