Search code examples
javascriptvue.jsfetches6-promise

How to Iterate Promise values and fill in the form?


I have a async function with return Fetch Request:

  async fetchDataByCodOS(codOS){      
    const res = await fetch(
      'http://localhost:5000/cods/'+codOS,
        {
          method:"GET"
        }
    ).then(res => res.json())
    return res
  }

I'm able to print a Promise Object, now I have to iterate it. By using a @input:change() in v-text-field

change(){
   this.fetchDataByCodOS(this.codOS).then( result => console.log(result) ); // Returning Object inside Array
}

I want to just grab the Object Json:

change(){
   this.fetchDataByCodOS(this.codOS).then( result => result.forEach(
       el => console.log(el)   
   ));
}

yeap, Now I can access the elements inside object, but I can't fill it my form, an example using v-model:

<v-text-field label="ID" v-model="ID" disabled> </v-text-field>

I thought which the right way to fill was create a ob = {} in data i could to use inside forEach, but I don't.

export default {
data(){
 return {
   ob: {
    //values here   
       }
  }
 }
}

Can you guys can suggest me a better practice/way to make this work?

Updating

VUE FORM:

      <v-text-field 
        label="Id. OS"
        v-model="codOS"
        @input="change()"  
      > 
      </v-text-field>
    </v-col>
    <v-col cols="12" md="9">
      <v-text-field label="ID"  disabled> </v-text-field>
    </v-col>
    <v-col cols="12" md="4">
      <v-text-field label="NAME"  disabled> </v-text-field>
    </v-col>
    <v-col cols="12" md="8">
      <v-text-field label="CITY"  disabled> </v-text-field>
    </v-col>                
    <v-col cols="12" md="9">
      <v-text-field label="FONE"  disabled> </v-text-field>
    </v-col>    

<script>
  export default {
    data (){
      return{
        codOS: '',
      }
    },
    methods:{
      autocomplete(){
        if(this.codOS.length == '5'){
            const data = this.fetchDataByCodOS(this.codOS);
            //data.then(r =>  console.log(r)); // Here I just grab the data object, but I'm trying to remove the array, so.
           data.then(r => r.forEach(el =>
              console.log(el) 
               //I think her I want to take el.nameObject and assign to v-model
            ))

        }
      },
      // Change my function according MAX
      async fetchDataByCodOS(codOS){      
        const res = await fetch(
          'http://localhost:5000/cods/'+codOS,
            {
              method:"GET"
            }
        )
        const json = await res.json()
        return json
      }
    }
  }
</script>

What console is showing:

Promise {<pending>}
__proto__: Promise
[[PromiseStatus]]: "resolved"
[[PromiseValue]]: Array(1)
0: {ID: "1", NAME: "EDDY", CITY: "NEW YORK", FONE: "6456465465456"}
length: 1
__proto__: Array(0)

Add.vue?5f53:71 
{ID: "1", NAME: "EDDY", CITY: "NEW YORK", FONE: "6456465465456"}
ID: "1"
NAME: "EDDY"
CITY: "NEW YORK"
FONE: "6456465465456"
__proto__: Object
   

Solution

  • Perhaps don't mix async/await with then syntax

    If the async/await syntax is available to you, you don't need to use the then function, as await/async when transpiles, transpiles down to the then function. So there's no need to mix them up here.

    • fetch() is asynchronous so must be awaited
    • json() is also asnychronous (gotcha! 😋) and must be awaited
      async fetchDataByCodOS(codOS){      
        const result = await fetch(
          'http://localhost:5000/cods/'+codOS, { method:"GET"}
        )
        const json = await result.json()
        return json
      }
    

    How to bind the data

    You can make an asynchronous call in the mounted method and set this on the data part of the component, which is then passed into the template.

    I've used the open trivia API which is a set to iterate, you can see how I iterated using v-for then after that you can use the handlebar type syntax

    
    <template>
      <div>
        <div v-for="(item) in results">
          <div style="border: 1px solid black; padding: 48px">
            <p>q: {{item.question}}</p>
            <p>a: {{item.correct_answer}}</p>
            <p> <input v-model="item.category"> </p> // binding inputs
          </div>
        </div>
      </div>
    </template>
    
    
      
    <script>
    export default {
      name: "App",
      components: {},
      props: ["initialResults"],
      data() {
        return {
          results: [{ question: "", category: "" }]
        };
      },
      async mounted() {
        var result = await fetch("https://opentdb.com/api.php?amount=10", {
          method: "GET"
        });
        var json = await result.json();
        this.results = json.results;
        console.log(json);
      }
    };
    </script>
    
    <style>
    #app {
      font-family: "Avenir", Helvetica, Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      text-align: center;
      color: #2c3e50;
      margin-top: 60px;
    }
    </style>
    
    
    

    Binding form inputs

    According to https://v2.vuejs.org/v2/guide/forms.html, you can bind a model directly to an input like this

    <input v-model="item.category">
    

    I got this similar working on a sandbox here https://codesandbox.io/s/max-vue-fetch-iterate-wc5qm?file=/src/App.vue

    I found the following links helpful in constructing my answer/learning how to do this

    https://www.sitepoint.com/fetching-data-third-party-api-vue-axios/ https://v2.vuejs.org/v2/guide/list.html https://v2.vuejs.org/v2/guide/forms.html