Abhilash
Abhilash

Reputation: 2953

unable to change data values when using $emit on an object in vue

Using $emit for communicating between child to parent component. I have a method in child component which trigger the $emit during diffferent stages of an api call. ie, before doing the api call I need to send few values to parent to disable some fields in parent. So using $emit to sent those values and it works successfully. But then when I try to call same $emit event after getting the result from api in same method it is not getting updated. The values are successfully getting emitted from child but in parent it only updates the first time.

Here is the parent component

<template>
  <div>
    <div class="horizontal-fields">
      <input
        id="General_mobile"
        class="input-fied"
        name="mobileNumber"
        placeholder="Enter your mobile"
        type="number"
        autocomplete="new mobile"
        :disabled="otpInput.isVerifying"
      >
      <input @click.prevent="sendVerificationCode" value="sendotp" class="otp-btn" type="button">
    </div>

    <div v-if="otpInput.isOtpMode">
      <GeneralOtp
        :message="otpGeneratedMessage"
        :mobileNumber="this.mobileNumber"
        @handleComplete="handleCompleteOtp"
      />
    </div>
  </div>
</template>

<script>
import axios from "axios";
import GeneralOtp from "./GeneralOtp";

export default {
  name: "MobileVerification",
  components: {
    GeneralOtp
  },
  data() {
    return {
      mobileNumber: null,
      isValidMobile: null,
      buttonValue: "Send OTP",
      otpGeneratedMessage: null,
      otpInput: {
        isOtpMode: false,
        isVerifying: false,
        otpToken: null,
        currentVerifiedMobile: null
      }
    };
  },
  methods: {
    async sendVerificationCode() {
      const { data } = await axios.get(
        "https://jsonplaceholder.typicode.com/todos/1"
      );
      if (data.userId) {
        this.otpGeneratedMessage = "some message from server";
        this.otpInput.isOtpMode = true; //to show otp input field(child component)
      }
    },
    handleCompleteOtp(value) {
      console.log("called");
      this.otpInput = value;
    }
  }
};
</script>

Here is the child component

    <template>
  <div>
    <div v-if="!isVerifying">
      <input id="otp" class="input-fied" name="otpcode" placeholder="Enter your otp" type="number">
      <input @click.prevent="this.verifyOtp" value="buttonValue" class="otp-btn" type="button">
      <p style="margin-top: 2%">{{ message }}</p>
    </div>
    <div v-else="isVerifying">
      <p>Please wait</p>
    </div>
  </div>
</template>

<script>
import axios from "axios";

export default {
  props: {
    message: {
      type: String,
      default: ""
    },
    mobileNumber: {
      type: String,
      default: null
    }
  },
  data() {
    return {
      isVerifying: false
    };
  },
  methods: {
    async verifyOtp() {
      /* Disable inputs & show loading */
      this.isVerifying = true;
      this.respondToParent({
        otpToken: null,
        mobileNumber: this.mobileNumber,
        isVerifying: this.isVerifying,
        isOtpMode: false
      });

      /* Send verify request to server */
      const { data } = await axios.get(
        "https://jsonplaceholder.typicode.com/todos/1"
      );
      /* If success & valid hide in parent send verified flag to parent */
      /* If success & invalid otp show error */
      this.isVerifying = false;
      if (data.userId) {
        this.respondToParent({
          otpToken: "token from a success response",
          mobileNumber: this.mobileNumber,
          isVerifying: false,
          isOtpMode: false
        });
      } else {
        this.respondToParent({
          OtpToken: null,
          mobileNumber: this.mobileNumber,
          isVerifying: this.isVerifying,
          isOtpMode: false
        });
      }
      /* If error show retry button with error message */
    },

    respondToParent(value) {
      this.$emit("handleComplete", {
        otpToken: value.otpToken,
        mobileNumber: this.mobileNumber,
        isVerifying: value.isVerifying,
        isOtpMode: value.isOtpMode
      });
    }
  }
};
</script>

I cant figure out why its not getting updated the second time even though its getting called both times from child. Somehow managed to replicate the same on sandbox environment. code in codesandbox

Upvotes: 0

Views: 1188

Answers (1)

ittus
ittus

Reputation: 22413

The first time you called this.respondToParent, it sets otpInput.isOtpMode to false, so GeneralOtp will not be rendered because you're using v-if:

<div v-if="otpInput.isOtpMode">
  <GeneralOtp
    :message="otpGeneratedMessage"
    :mobileNumber="this.mobileNumber"
    @handleComplete="handleCompleteOtp"
  />
</div>

You can check it will be called 2 times if you change your first this.respondToParent to

  this.respondToParent({
    otpToken: null,
    mobileNumber: this.mobileNumber,
    isVerifying: this.isVerifying,
    isOtpMode: true
  });

(Note that isOtpMode: true)

I think you should keep isOtpMode is true in the first call and use isVerifying to disable something in the parent component as you said.

Demo here

Upvotes: 2

Related Questions