2

Here is the demo response from the API:

[
    {
      form_name: 'Form One',
      name: 'Peter',
      email: '[email protected]'
    },
    {
      form_name: 'Form Two',
      name: 'John',
      email: '[email protected]'
    }
  ]

From that I built a form through v-for

<v-form 
   v-for="(item, index) in forms" :key="index"
>
  <v-btn
    :loading="saveLoading"
    @click="submit(item)"
  >
    submit
  </v-btn>
</v-form>

I set saveLoading: false, initially. In the submit(item) function, I was setting saveLoading: true, at first. Then at asynchronous function for POST method, I would turned that back to false. But, the problem is whenever, I click on the submit button in one form, two submit buttons in the two form got affected by loading state. enter image description here

What to do to get the effect in one submit button for the current form only?

Codepen Demo

4 Answers 4

1
<v-btn
    class="mr-4"
    :loading="saveLoading == index"
    @click="submit(item, index)"
  >


data: () => ({
    valid: true,
    saveLoading: -1,
 })


submit (formItem, index) {
      this.saveLoading = index
      console.log(formItem)
      // POST the formItem and make the saveLoading false in async then(), catch()
    },

It is not a very logical method, but this way you can solve the problem.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for your tricky solution. The problem is this when I click on one button, it gets the loading state. That's fine. But, whenever, I click on the other button in another form, the loading state from the first button has gone. If in any case, the API response is getting delayed after clicking the first button, and at that time if anyone clicks on the second button, the first button will be back to normal state even the response got stuck for any reason.
1

The problem is that you have only one variable. You could do saveLoading1 to saveLoadingN but that's not very useful.

var forms = [
    {
      form_name: 'Form One',
      name: 'Peter',
      email: '[email protected]'
    },
    {
      form_name: 'Form Two',
      name: 'John',
      email: '[email protected]'
    }
  ]
function generate_computed_properties() {
  var states = {};
  for (let [ index, item ] of forms.entries()) {
    states['saveLoading_' + index] = false
  }
  return { states }
}

new Vue({
  el: '#app',
  data() {
    return {
      forms: window.forms,
    ...generate_computed_properties()
    }
  },
  methods: {
    submit(index) {
        this.setLoadingState(index, true)
        setTimeout((function() {
          this.setLoadingState(index, false)
        }).bind(this), 1000)
    },
    setLoadingState(index, state) {
        this.states['saveLoading_' + index] = state
    },
  },
  computed: {
  },
  template: `<div>
    <form 
     v-for="(item, index) in forms" :key="index"
  >
      <h2>{{ item.form_name }}</h2>
      <button
        :disabled="states['saveLoading_' + index]"
        @click.prevent="submit(index)"
      >
        <span v-if="states['saveLoading_' + index]">Loading &hellip;</span>
        <span v-else>submit</span>
      </button>
  </form>
</div>`,
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>

It'll be better to split your forms into subcomponents:

var forms = [
    {
      form_name: 'Form One',
      name: 'Peter',
      email: '[email protected]'
    },
    {
      form_name: 'Form Two',
      name: 'John',
      email: '[email protected]'
    }
  ];
var Button = Vue.component('v-btn', {
  props: [ 'loading' ],
  template: `<button :disabled="loading" @click="handleClick">
    <slot v-if="!loading"></slot>
    <span v-else>Saving &hellip;</span>
  </button>`,
  methods: {
    handleClick() {
      this.$emit('click');
    }
  }
})
  
var Form = Vue.component('v-form', {
  name: "Form",
  props: [ 'form' ],
  data() {
    return {
      saveLoading: false
    }
  },
  template: `<div>
    <h2>{{ form.form_name }}</h2>
    <v-btn
      :loading="saveLoading"
      @click="submit(form)"
    >
      submit
    </v-btn>
  </div>`,
  methods: {
    submit() {
      console.log('save')
      this.setLoading(true);
      setTimeout(this.setLoading.bind(this, false), 1000)
    },
    setLoading(isLoading) {
      this.saveLoading = isLoading
    }
  }
});

new Vue({
  el: '#app',
  components: { Form },
  data() {
    return {
      forms: window.forms
    }
  },
  template: `<div>
    <v-form 
     v-for="(item, index) in forms" :key="index" :form="item"
  >
  </v-form>
</div>`
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>

3 Comments

Thanks for your solution. But, unfortunately, I am not in a state of changing the structure i.e. breaking into sub-components as the real scenario is not the same as this simple example. So, I was looking for a simple javascript example. For example, I was thinking if it's possible to take an array object dynamically something (for example, loadingArr) like this; [{saveLoading: false, clearLoading: false}, {saveLoading: false, clearLoading: false}]. Then, I can create the function like this way: submit(item, index) {this.loadingArr[index].saveLoading = true}
I added an alternative (first code block)
Based on the concept of your alternative solution, I have tried to simplify it according to my codes to make it working. But, it's not working. Can you please take a look on this CodePen demo: codepen.io/lr_73/pen/GRMvmZz?editors=10, modify it with that structure and provide the working CodePen link if possible?
0

If you are generating the forms from the api response, mean that in some way you are attaching the response to the Vue Data property. If so, you could easily enrich the objects of the array (the forms object) to have a isLoading property. So the result will be something like:

API RESPONSE
[
    {
      form_name: 'Form One',
      name: 'Peter',
      email: '[email protected]'
    },
    {
      form_name: 'Form Two',
      name: 'John',
      email: '[email protected]'
    }
  ]

and store is as:

Stored object
[
    {
      form_name: 'Form One',
      name: 'Peter',
      email: '[email protected]',
      isLoading: false
    },
    {
      form_name: 'Form Two',
      name: 'John',
      email: '[email protected]',
      isLoading: false
    }
  ]

Set the loading status in the submit(item) method:

submit(item){
   item.isLoading = true;
}

then you can use it by checking the value of isLoading on the form object, something on the lines of:

<v-form 
   v-for="(item, index) in forms" :key="index"
>
  <v-btn
    :loading="item.isLoading"
    @click="submit(item)"
  >
    submit
  </v-btn>
</v-form>

1 Comment

Thanks; that would be easy. But, I can't add a property at that array Objects. Becasue, I simply send that array of objects at the submit function. And the POST API can't accept those additional properties. So, I need to remove those properties at the time of submission. But, at then() callback how will I remove those when I send the data to the POST api before that callback and maintain the loading status at the callback? So, I may think of a new array that will be created dynamically matching with the length of the array from the response. Please, see my comment on 1st solution here.
0

Just create an inner component and use on for loop:

<v-form 
   v-for="(item, index) in forms" :key="index">
        <inner-component :item="item"></inner-component>
</v-form>

And in inner component you have:

<v-btn
    :loading="loading"
    @click="submit(item)"
  >
    submit
</v-btn>
<script>

  props: {
    item:{
      type: Object,
      required: true,
    }
  },
  data() {
    return {
      loading: false
    }
  },
  methods:{
    submit(item){
      this.loading = true;
      // Do anything you want 
    }
  }

</script>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.