Search code examples
vue.jsvuejs2axiosparent-childvue-multiselect

How do I update my parent component data whenever my child component adds new data


BACKGROUND:

I am adding new actor data from inside the modal (which has the child component) and in return whenever the child component adds new actor the multiselect which is in the parent component should be updated with the newly added value.

CODE:

Moviecreatecomponent.vue (parent component)

<template>
  <div class="container">
    <br />
    <!-- Dynamic alert tag for displaying success and failure messages -->
    <b-alert
      :show="dismissCountDown"
      dismissible
      :variant="alertVariant"
      @dismissed="dismissCountDown=0"
      @dismiss-count-down="countDownChanged"
    >{{alertMessage}}</b-alert>

    <div class="row">
      <!-- Actor modal button -->
      <b-button
        @click="actorModalShow = !actorModalShow"
        style="float:left"
        class="btn btn-info"
      >Add Actor</b-button>&nbsp;&nbsp;&nbsp;
      <!-- Actor modal -->
      <b-modal v-model="actorModalShow" title="Add Actor" hide-footer>
        <!-- common component for actor/producer (actor) -->
        <app-person-create
          @closeActorModal="closeActorModal"
          childtype="Actor"
          @alertFromChild="showAlert"
        />
      </b-modal>

      <!-- Producer modal button -->
      <b-button
        @click="producerModalShow = !producerModalShow"
        style="float:right"
        class="btn btn-info"
      >Add Producer</b-button>&nbsp;&nbsp;&nbsp;
      <!-- Producer modal-->
      <b-modal v-model="producerModalShow" title="Add Producer" hide-footer>
        <!-- common component for actor/producer (producer) -->
        <app-person-create
          @closeProducerModal="closeProducerModal"
          childtype="Producer"
          @alertFromChild="showAlert"
        />
      </b-modal>
    </div>
    <br />

    <!-- form component -->
    <form>
      <div class="row">
        <div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-6">
          <!-- Movie name -->
          <div class="form-group">
            <label for="moviename">Movie Name:</label>
            <input
              id="moviename"
              name="moviename"
              v-validate="'required'"
              data-vv-validate-on="validateStep"
              v-model="movieData.name"
              type="text"
              class="form-control"
            />
            <span class="error" v-show="errors.has('moviename')">{{ errors.first('moviename') }}</span>
          </div>
          <!--Year Of Release -->
          <div class="form-group">
            <label for="yearOfRelease">Year Of Release:</label>
            <input
              id="yearOfRelease"
              name="yearofrelease"
              v-model="movieData.yearOfRelease"
              type="date"
              class="form-control"
              v-validate="'required'"
              data-vv-validate-on="validateStep"
            />
            <span
              class="error"
              v-show="errors.has('yearofrelease')"
            >{{ errors.first('yearofrelease') }}</span>
          </div>
        </div>
      </div>
      <div class="row">
        <!-- Plot -->
        <div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-6 col-md-offset-3 form-group">
          <label for="plot">Plot:</label>
          <br />

          <textarea
            id="plot"
            name="plot"
            v-validate="'required'"
            data-vv-validate-on="validateStep"
            v-model="movieData.plot"
            rows="5"
            class="form-control"
          />
          <span class="error" v-show="errors.has('plot')">{{ errors.first('plot') }}</span>
        </div>
      </div>
      <div class="row">
        <!-- Poster -->
        <div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-6 col-md-offset-3 form-group">
          <label for="poster">Poster:</label>
          <br />
          <input
            id="poster"
            name="poster"
            v-model="movieData.poster"
            v-validate="'required'"
            data-vv-validate-on="validateStep"
            type="text"
            class="form-control"
          />
          <span class="error" v-show="errors.has('poster')">{{ errors.first('poster') }}</span>
        </div>
      </div>
      <div class="row">
        <!-- Actors -->
        <div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-6 col-md-offset-3 form-group">
          <label for="actors">Actors:</label>
          <br />
          <multiselect
            v-model="movieData.actors"
            :options="actorNameDataFromJson"
            :multiple="true"
            label="name"
            track-by="id"
          />
        </div>
      </div>

      <hr />
      <div class="row">
        <!-- Producers -->
        <div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-6 col-md-offset-3 form-group">
          <label for="actors">Producers:</label>

          <multiselect
            v-model="movieData.producers"
            :options="producerNameDataFromJson"
            :multiple="true"
            label="name"
            track-by="id"
          />
        </div>
      </div>
      <div class="row">
        <div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-6 col-md-offset-3">
          <button class="btn btn-primary" @click.prevent="validateBeforeSubmit">Submit!</button>
        </div>
      </div>
    </form>
    <hr />
  </div>
</template>

<script>
import axios from "axios";
import PersonCreate from "../Person/PersonCreateComponent";

export default {
  components: {
    "app-person-create": PersonCreate
  },
  data: function() {
    return {
      //used to retrieve actor data for multiselect options
      actorNameDataFromJson: [],
      //used to retrieve producer data for multiselect options
      producerNameDataFromJson: [],
      //objects for alerts
      dismissSecs: 5,
      dismissCountDown: 0,
      alertVariant: "",
      alertMessage: "",
      //objects used for modals
      actorModalShow: false,
      producerModalShow: false,
      //objects used for all the inputs
      movieData: {
        id: 0,
        name: "",
        yearOfRelease: "",
        plot: "",
        poster: "",
        actors: [], //selected actors
        producers: [] //selected producers
      }
    };
  },
  created() {
    //data for the edit part
    console.log("Created");
    if (this.$route.params.id) {
      axios
        .get(`http://195.201.189.119:63790/movies/` + this.$route.params.id)
        .then(res => {
          this.movieData = res.data;
        });
    }

    this.loadActorMultiselect();
    this.loadProducerMultiselect();
  },

  methods: {
    // clears the form
    clearform() {
      this.movieData.id = null;
      this.movieData.name = null;
      this.movieData.yearOfRelease = null;
      this.movieData.plot = null;
      this.movieData.poster = null;
      this.movieData.actors = null;
      this.movieData.producers = null;
    },

    //form submit
    submited() {
      this.submitedform = true;
      let data = this.movieData;
      // checks if the id is equal to initial value
      if (this.moviedata.id == 0) {
        axios
          .post("http://195.201.189.119:63790/movies", this.moviedata)
          .then(res => {
            console.log(res.status);

            this.clearform();
          })
          .catch(error => {
            console.log(error);
            this.callDangerAlert();
          });
        this.callSuccessAlert();
      }
      //if not then it will update the data
      else {
        axios
          .put(
            `http://195.201.189.119:63790/movies/` + this.moviedata.id,
            this.moviedata
          )
          .then(res => {
            console.log(res.status);
            this.clearform();
          })
          .catch(error => {
            console.log(error);
            this.callDangerAlert();
          });
        this.callSuccessAlert();
      }
      console.log(data, "Final form submitted data");
    },

    //methods for alerts
    callSuccessAlert() {
      console.log("I am here success");
      this.childAlert.variant = "success";
      this.childAlert.message = "Data Recorded Successfully!!";
      this.$emit("alertFromChild", this.childAlert);
      console.log();
    },
    callDangerAlert() {
      this.childAlert.variant = "danger";
      this.childAlert.message = "Something Went Wrong!!";
      this.$emit("alertFromChild", this.childAlert);
    },
    // method to close producer modal from child button
    closeProducerModal() {
      this.producerModalShow = false;

      //reloads producer multiselect
      this.loadProducerMultiselect();
    },

    // method to close actor modal from child button
    closeActorModal() {
      this.actorModalShow = false;
      // reloads actor multiselect
      this.loadActorMultiselect();
    },

    //Loads actor multiselect
    async loadActorMultiselect() {
      //gets all actor names from actor json and formats it as {id:,name:}

      try {
        let response = await axios.get("http://195.201.189.119:63790/actors");
        let data = response.data;
        let actorNamesOption = [];

        for (let i = 0; i < data.length; i++) {
          if ((data[i].id && data[i].name) || data[i].personname) {
            actorNamesOption.push({
              id: data[i].id,
              name: data[i].name || data[i].personname
            });
          }
        }
        this.actorNameDataFromJson = actorNamesOption;
      } catch (error) {
        console.log(error);
      }
    },
    async loadProducerMultiselect() {
      //gets all producers names from producers json and formats it as {id:,name:}
        try {
        let response = await axios.get("http://195.201.189.119:63790/producers");
        let data = response.data;
        let producerNamesOption = [];
        for (let row in data) {
          if ((data[row].id && data[row].name) || data[row].personname) {
            producerNamesOption.push({
              id: data[row].id,
              name: data[row].name || data[row].personname
            });
          }
        }
        this.producerNameDataFromJson = producerNamesOption;
      } catch (error) {
        console.log(error);
      }
    },
    //setting countdown variables
    countDownChanged(dismissCountDown) {
      this.dismissCountDown = dismissCountDown;
    },
    //dynamic alert component for success and failure
    showAlert(alertDataFromchild) {
      this.alertVariant = alertDataFromchild.variant;
      this.alertMessage = alertDataFromchild.message;
      this.dismissCountDown = this.dismissSecs;
    },
    // validates for all the fields and calls submited method
    validateBeforeSubmit() {
      this.$validator.validateAll().then(result => {
        if (result) {
          this.submited();
          return;
        }
        alert("Please enter all the details!");
      });
    }
  }
};
</script>
  <style src="vue-multiselect/dist/vue-multiselect.min.css"></style>
  <style>
.error {
  color: red;
}
.childbutton {
  float: right;
  margin-left: 5px;
}
</style>

Personcreatecomponent.vue (child component)

<template>
  <div>
    <form @submit.prevent="validateBeforeSubmit">
      <div class="row">
        <div class="col-xs-12 col-sm-12">
          <div class="form-group">
            <label for="personname">{{childtype}} Name:</label>
            <input
              id="personname"
              v-model="personData.personname"
              type="text"
              name="personname"
              class="form-control"
              v-validate="'required|alpha'"
            >
            <span class="error" v-show="errors.has('personname')">{{ errors.first('personname') }}</span>
          </div>

          <div class="form-group">
            <label for="bio">{{childtype}}'s gender: &nbsp;</label>
            <label for="male">
              <input
                id="male"
                v-model="personData.sex"
                name="genderradiogroup"
                v-validate="'required|included:Male,Female'"
                type="radio"
                value="Male"
              > Male
            </label>
            &nbsp; &nbsp; &nbsp;
            <label for="female">
              <input
                id="female"
                v-model="personData.sex"
                name="genderradiogroup"
                type="radio"
                value="Female"
              > Female
            </label>
            <br>
            <span
              class="error"
              v-show="errors.has('genderradiogroup')"
            >{{ errors.first('genderradiogroup') }}</span>
          </div>

          <div class="form-group">
            <label for="bio">{{childtype}}'s bio:</label>
            <br>

            <textarea
              id="bio"
              name="bio"
              v-validate="'required|alpha'"
              v-model="personData.bio"
              rows="5"
              class="form-control"
            />
            <span class="error" v-show="errors.has('bio')">{{ errors.first('bio') }}</span>
          </div>

          <div class="form-group">
            <b-button variant="success" class="childbutton" @click="validateBeforeSubmit">Submit</b-button>
            <b-button variant="danger" class="childbutton" @click="closepersonmodal">Cancel</b-button>
          </div>
        </div>
      </div>
    </form>
  </div>
</template>

<script>
import axios from "axios";
export default {
  data: function() {
    return {
      personData: {
        personname: "",
        sex: "",
        bio: ""
      },
      childAlert: {
        variant: "",
        message: ""
      }
    };
  },

  props: ["childtype"],
  methods: {
    //method used to get all fields that are entered in person modal
    personsubmited() {
      if (this.childtype == "Actor") {
        console.log(this.childtype);

        axios
          .post("http://195.201.189.119:63790/actors", this.personData)
          .then(res => {
            console.log(res.status);
          })
          .catch(error => {
            console.log(error);
            this.callDangerAlert();
          });
        this.callSuccessAlert();
      } else if (this.childtype == "Producer") {
        console.log(this.childtype);
        axios
          .post("http://195.201.189.119:63790/producers", this.personData)
          .then(res => {
            console.log(res.status);
          })
          .catch(error => {
            console.log(error);
            this.callDangerAlert();
          });
        this.callSuccessAlert();
      }

      this.$emit("close" + this.childtype + "Modal",this.personData);
    },
    validateBeforeSubmit() {
      this.$validator.validateAll().then(result => {
        if (result) {
          this.personsubmited();
          return;
        }
        alert("Please enter all the details!");
      });
    },
    callSuccessAlert() {
      console.log("I am here success");
      this.childAlert.variant = "success";
      this.childAlert.message = "Data Recorded Successfully!!";
      this.$emit("alertFromChild", this.childAlert);
      console.log();
    },
    callDangerAlert() {
      this.childAlert.variant = "danger";
      this.childAlert.message = "Something Went Wrong!!";
      this.$emit("alertFromChild", this.childAlert);
    },
    closepersonmodal() {
      this.$emit("close" + this.childtype + "Modal");
    }
  }
};
</script>

<style>
.error {
  color: red;
}
.childbutton {
  float: right;
  margin-left: 5px;
}
</style>

I am new to vue and I don't know where am I going wrong, any suggestions, hints would be helpful.


Solution

  • First => To send data from child to parent : in child component use in your script

    this.$emit('your-event-name',value) 
    

    and in parent component use:

    <child-component
              @your-event-name="event-handler"
            />
    

    Second => to send data from parent to child: in parent compenent use prop ":"

    <child-component
              :propName="value"
            />
    

    in child component use :

    props: ["propName"]