javascript - Angular Reaktivt Formulär Redigeringsläge, Hur fyller man på Input typ=file igen?


Hur kan jag återfylla min fil med input type=file inom ett reaktivt formulär?

Jag arbetar på ett projekt där jag har ett formulär för att skicka data (träningspass) som bygger upp en lista enligt formuläret. Därifrån kan du gå in för att se det inskickade träningspasset och redigera det.

När du klickar på "Redigera Träning" öppnas samma inlämningsformulär, denna gång fyllt med all data. Detta görs genom att länka "Redigera Träning" till EditComponent, och sedan kontrollerar ngOnInit ruttparametrarna. Om ruttparametrarna har ett id, så initialiseras formuläret med träningsdata.

Allt detta fungerar helt fint.

Problemet jag har handlar specifikt om återbefolkningen av <input type="file"...>.

Här är min formulärinitiering när jag går för att redigera Först sätter jag alla variabler tomma i initForm(), sedan om editMode är sant (om jag redigerar), kommer det att fylla var och en av dessa tomma variabler med data som kommer från servern (eller dummydata).

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)
  });
}

När jag gör detta för ett nytt träningspass (standardform), finns det inget problem. Problemet uppstår när jag är i redigeringsläge och försöker fylla i data i <input type="file"...> som nämnts ovan.

Just nu kommer min workoutImageUrl eller workout.imageUrl från en URL som ges från min FireBase Storage, så jag kan se varför det finns ett problem här med inputtypen. Samma sak händer om jag använder en lokal fil, den kan inte fyllas på igen till inputtypen för fil.

Jag föreställer mig att detta beror på säkerhetsproblem, men detta orsakar att mitt formulär får följande fel: från Chrome: "ERROR DOMException: Misslyckades med att sätta egenskapen 'value' på 'HTMLInputElement': Detta inmatningselement accepterar ett filnamn, vilket endast kan ställas in programmässigt till den tomma strängen." från FF: "ERROR DOMException: "Ett försök gjordes att använda ett objekt som inte är, eller inte längre är, användbart"

Finns det något sätt jag kan "återbefolka" min bild när jag redigerar ett träningspass? Så att filen visas som det aktuella objektet inom träningspasset?

Som nämnts ovan har jag försökt använda lokala bildfiler vilket ger samma resultat. Jag har också försökt ta bort imageUrl från formgruppen helt, men det hjälper inte eftersom jag vill att den ska vara i formgruppen. Jag har också försökt ändra workoutImageUrl till att vara samma som imgSrc som kommer från Firebase. Detta fungerar om jag vill visa den nuvarande träningsbilden, men det fungerar inte när jag skickar in eftersom jag får ett fel att den nuvarande bilden är null.

Här är en StackBlitz för detta projekt (lite förenklat). Om du klickar på ett träningspass i listan kan du sedan välja "hantera träningspass" och "redigera träningspass" för att komma till redigeringssidan med ifyllt formulär (fungerar för närvarande inte med bilduppladdningen).

Komponenten WorkoutEditComponent kan hittas via src/app/features/workouts-page/workout-edit. Här kan du se logiken för bilduppladdningen, men jag tror inte att detta är relevant för just detta problem. Databaslagringen fungerar inte på StackBlitz, men uppladdningen av bilderna fungerar (det sparas bara inte). Jag lade in ett exempel på en API för att få detta att fungera minimalt för denna StackBlitz.

WorkoutsListComponent är där listan över inlämnade träningspass visas.

Finns det något sätt att behålla denna träningsbildsfil när jag tittar på den medan jag redigerar ett träningspass? Och samtidigt slippa ladda upp bilden igen när jag redigerar ett formulär? Jag hade bilden inställd på att visas längst ner på redigeringssidan, men om den visas här laddas den fortfarande inte upp igen eller använder samma bild från servern. Jag skulle behöva ladda upp bilden varje gång.

Vänligen låt mig veta om det finns något jag kan förklara eller utveckla.

Svar #1

Det du behöver är att återskapa objektfilen från bildadressen och sedan tilldela den till inmatningsfilen.

Jag kom på den här idén :

@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: Jag har provat det på ditt Stackblitz-exempel och det fungerar, jag hoppas att detta hjälper. Lycka till!

Ett annat sätt att fixa CORS-problemet är att använda detta:

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

Svar #2

Jag föreslår att skapa en funktion som returnerar en 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)
    });
}

du använder

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