Search code examples
vue.jsvuejs3vue-composition-apivue-sfc

Edit form in Vue with SFC


I want to create a edit form that populates the form with existing data from a API using fetch, then lets the user edit the form and submit the form data to a API using a PUT request.

Requirements:

  • Composition API not Options API
  • Single-file components SFC with <script setup> not <script>
  • async/await syntax not resolve promises using .then()
  • TypeScript

Here is the code I have, but I haven't figured it all out.

<script setup lang="ts">
import { ref, reactive } from 'vue'; // ref or reactive?

interface Profile {
  firstName: string;
  lastName: string;
}

const profile = reactive<Profile | null>(null); // initial state is null before we fetch?
//profile.value = await fakeFetch(); // after fetch we need to update the state

function onSubmit() {
  alert(JSON.stringify(profile));
}

function fakeFetch(): Promise<Profile> {
  const data: Profile = {
    firstName: 'Alice',
    lastName: 'Smith',
  };
  return new Promise(resolve => resolve(data));
}
</script>

<template>
  <h1>Your user profile</h1>
  <p v-if="profile">You have a profile</p>
  <p v-else>The profile does not exist</p>
  <!-- v-if="profile" because profile is null before fetch has finished? -->
  <form @submit.prevent="onSubmit" v-if="profile">
    <input type="text" v-model="profile.firstName" />
    <input type="text" v-model="profile.lastName" />
    <input type="submit" value="Update" />
  </form>
</template>

You can test/experiment with my code at https://stackblitz.com/edit/vitejs-vite-iyy2pu?file=src%2Fcomponents%2FUserProfile.vue&terminal=dev


Solution

  • I figured out how to do this.

    I use ref not reactive, and have to put the async fetch(...) code inside onMounted.

    <script setup lang="ts">
    import { ref, onMounted } from 'vue';
    
    interface Post {
      firstName: string;
      lastName: string;
    }
    
    const profile = ref<Post | null>(null);
    
    onMounted(async () => {
      const data = await fakeFetch();
      profile.value = data;
    });
    
    function onSubmit() {
      alert(JSON.stringify(profile.value));
    }
    
    function fakeFetch(): Promise<Post> {
      const data: Post = {
        firstName: 'Alice',
        lastName: 'Smith',
      };
      return new Promise(resolve => resolve(data));
    }
    </script>
    
    <template>
      <h1>Your user profile</h1>
      <p v-if="profile">You have a profile</p>
      <p v-else>The profile does not exist</p>
      <form @submit.prevent="onSubmit" v-if="profile">
        <input type="text" v-model="profile.firstName" />
        <input type="text" v-model="profile.lastName" />
        <input type="submit" value="Update" />
      </form>
    </template>