Felipe Kosouski
Felipe Kosouski

Reputation: 69

How to dynamically add/remove a new form row with select/autocomplete values in vuetify?

i've been struggling for some time with a dynamic form using Vuejs and Vuetify. First of all, i have an API that returns me the value for competences and skills to add to an appraisal. When creating a new appraisal i want to show the form in steps, so i'm using v-stepper. The problem is, in the third step, the user need to select one of the competences that he already selected before, and after he select this competence, he needs to add skills to this competence, so one competence can have multiple skills.

What i'm trying to do is, in step 2 I have a select that fills the selectedCompetences array, and in step 3, i have a selectedCompetence property, that holds the competence the user selected for adding skills, and a selectedSkills array, to add to this competence.

To post to the api i need an array like

[
  competenceId: 1
  skills: 
  [
   {skillId: 1},
   {skillId: 2},
   {skillId: 3},
   {skillId: 4},
  ]
]

And, i need to do this like, the user adds the first set of competences/skills. And he wants to add skills to another competence, so it needs to have a button to add 2 new fields to the form for him to fill the same way. I lost an entire day trying to figure out this logic, but nothing seems to work.

Below is my stepper component:

<template>
  <v-stepper v-model="step" alt-labels>
    <v-stepper-header>
      <v-stepper-step editable step="1" :complete="step > 1">Avaliação</v-stepper-step>
      <v-divider></v-divider>

      <v-stepper-step editable step="2" :complete="step > 2">Competências</v-stepper-step>
      <v-divider></v-divider>

      <v-stepper-step editable step="3" :complete="step > 3">Fatores</v-stepper-step>
      <v-divider></v-divider>

      <v-stepper-step step="4">Resumo</v-stepper-step>
    </v-stepper-header>

    <v-stepper-content step="1">
      <v-row align="center" justify="center">
        <v-col cols="4">
          <v-text-field label="Descrição"></v-text-field>
        </v-col>
      </v-row>

      <v-row align="center" justify="center">
        <v-col cols="4">
          <v-text-field v-model="newAppraisal.year" label="Ano"></v-text-field>
        </v-col>
      </v-row>

      <v-row align="center" justify="center">
        <v-col cols="2">
          <v-menu
            v-model="menu_std"
            :close-on-content-click="false"
            :nudge-right="40"
            transition="scale-transition"
            offset-y
            min-width="290px"
          >
            <template v-slot:activator="{ on }">
              <v-text-field label="Data de Início" readonly v-on="on" :value="computedStartDate"></v-text-field>
            </template>
            <v-date-picker
              v-model="newAppraisal.startDate"
              show-week
              @input="menu_std = false"
              color="primary"
            ></v-date-picker>
          </v-menu>
        </v-col>

        <v-col cols="2">
          <v-menu
            v-model="menu_end"
            :close-on-content-click="false"
            :nudge-right="40"
            transition="scale-transition"
            offset-y
            min-width="290px"
          >
            <template v-slot:activator="{ on }">
              <v-text-field label="Data de Término" readonly v-on="on" :value="computedEndDate"></v-text-field>
            </template>
            <v-date-picker
              v-model="newAppraisal.endDate"
              show-week
              @input="menu_end = false"
              color="primary"
            ></v-date-picker>
          </v-menu>
        </v-col>
      </v-row>

      <v-row align="center" justify="center">
        <v-col cols="2">
          <v-radio-group v-model="newAppraisal.type" row>
            <v-radio label="Obrigatória" value="0" color="primary"></v-radio>
            <v-radio label="Pontual" value="1" color="primary"></v-radio>
          </v-radio-group>
        </v-col>
        <v-col cols="2">
          <div v-if="newAppraisal.type === '1'">
            <v-select label="Selecione o usuário" chips v-model="newAppraisal.appraised"></v-select>
          </div>
        </v-col>
      </v-row>

      <v-row>
        <v-col cols="12">
          <v-row align="center" justify="end">
            <v-btn color="success" tile large @click.native="next">Continuar</v-btn>
          </v-row>
        </v-col>
      </v-row>
    </v-stepper-content>

    <v-stepper-content step="2">
      <v-row align="center" justify="center">
        <v-col cols="4">
          <v-select
            v-model="selectedCompetences"
            :items="competences"
            multiple
            item-text="name"
            return-object
            label="Selecione as competências"
            chips
            hint="Adicione as competências para essa avaliação"
            persistent-hint
          ></v-select>
        </v-col>
      </v-row>

      <v-row>
        <v-col cols="12">
          <v-row align="center" justify="space-between">
            <v-btn color="error" tile large @click.native="previous">Voltar</v-btn>
            <v-btn color="success" tile large @click.native="next">Continuar</v-btn>
          </v-row>
        </v-col>
      </v-row>
    </v-stepper-content>

    <v-stepper-content step="3">
      <v-row align="center" justify="center">
        <v-col cols="8">
          <v-subheader class="title">Competencia 1</v-subheader>

          <v-divider></v-divider>
        </v-col>
      </v-row>
      <v-row align="center" justify="center">
        <v-col cols="4">
          <v-autocomplete
            :items="selectedCompetences"
            item-text="name"
            item-value="competenceId"
            v-model="selectedCompetence"
            label="Competência"
          ></v-autocomplete>
        </v-col>
      </v-row>

      <v-row align="center" justify="center">
        <v-col cols="4">
          <v-select
            :items="skills"
            v-model="selectedSkills"
            multiple
            chips
            label="Fatores"
            item-text="name"
            item-value="skillId"
          ></v-select>
        </v-col>
      </v-row>

      <v-row>
        <v-col cols="12">
          <v-row align="center" justify="space-between">
            <v-btn color="error" tile large @click.native="previous">Voltar</v-btn>
            <v-btn color="success" tile large @click.native="next">Continuar</v-btn>
          </v-row>
          <v-btn color="success" @click.native="addSkillsToCompetence">teste aqui</v-btn>
        </v-col>
      </v-row>
    </v-stepper-content>

    <v-stepper-content step="4">
      <v-list>
        <v-list-item>{{newAppraisal.year}}</v-list-item>
        <v-list-item>{{computedStartDate}}</v-list-item>
        <v-list-item>{{computedEndDate}}</v-list-item>
        <v-list-item v-if="newAppraisal.type === '0'">Obrigatória</v-list-item>
        <v-list-item v-else-if="newAppraisal.type === '1'">Pontual</v-list-item>
        <v-list-item
          v-for="(competence, index) in selectedCompetences"
          :key="index"
        >{{competence.name}}</v-list-item>
      </v-list>
      <v-btn color="primary" @click.native="previous">Voltar</v-btn>
      <v-btn color="primary" @click.native="save">Salvar</v-btn>
    </v-stepper-content>
  </v-stepper>
</template>

<script>
import moment from "moment";
export default {
  data: () => ({
    step: 1,
    menu_std: false,
    menu_end: false,
    newAppraisal: {
      year: "",
      startDate: new Date().toISOString().substr(0, 10),
      endDate: new Date().toISOString().substr(0, 10),
      type: "0",
      appraised: {
        name: "",
        sector: "",
        admissionDate: new Date().toISOString().substr(0, 10),
        jobTitle: "",
        appraiserName: ""
      }
    },
    competences: [],
    selectedCompetences: [],
    selectedCompetence: "",
    competenceSkills: [],
    skills: [],
    selectedSkills: [],
    errors: [],
    teste: {
      competenceId: "",
      skills: []
    }
  }),
  computed: {
    computedStartDate() {
      return this.newAppraisal.startDate
        ? moment(this.newAppraisal.startDate).format("DD/MM/YYYY")
        : "";
    },
    computedEndDate() {
      return this.newAppraisal.endDate
        ? moment(this.newAppraisal.endDate).format("DD/MM/YYYY")
        : "";
    }
  },
  methods: {
    previous() {
      this.step--;
    },
    next() {
      this.step++;
    },
    save() {
      // first save appraisal, then add skill to competence, then add competence to appraisal
    },
    loadCompetences() {
      axios
        .get("/questionnaire/competences")
        .then(response => {
          this.competences = response.data;
        })
        .catch(e => {
          this.errors.push(e);
        });
    },
    loadSkills() {
      axios
        .get("/questionnaire/skills")
        .then(response => {
          this.skills = response.data;
        })
        .catch(e => {
          this.errors.push(e);
        });
    },
    addSkillsToCompetence() {
      console.log(this.selectedCompetence, this.selectedSkills);
      this.teste.competenceId = this.selectedCompetence
      this.teste.skills = this.selectedSkills
      console.log(this.teste);
      this.competenceSkills.push(this.teste)

    }
  },
  created() {
    this.loadCompetences();
    this.loadSkills();
  }
};
</script>

<style>
</style>

If someone could help with this, i'm stuck

Upvotes: 0

Views: 1803

Answers (1)

User 28
User 28

Reputation: 5158

I'm not sure I understand you correctly but this is my solution:

<div id='app'>
  <v-app>
    <v-container>
      <v-stepper
        vertical
        v-model='step'>
        <v-stepper-step
          editable
          key='step-1'
          :step='1'
          :complete='step > 1'>
          Step 1
        </v-stepper-step>
        <v-stepper-content
          key='content-1'
          :step='1'>
          <v-select
            multiple
            label='Select Competencies'
            v-model='selectedCompetencies'
            :items='competencies'/>
        </v-stepper-content>
        <v-stepper-step
          editable
          key='step-2'
          :step='2'
          :complete='step > 2'>
          Step 2
        </v-stepper-step>
        <v-stepper-content
          key='content-2'
          :step='2'>
          <div v-for='set in selectedSkillSets'>
            <div>{{ set.competence }}</div>
            <v-select
              multiple
              label='Add Skill'
              v-model='set.skills'
              :items='skills'/>
          </div>
          <v-select
            label='Add Skill Set'
            v-if='availableCompetencies.length'
            :items='availableCompetencies'
            @change='addSkillSet($event)'/>
        </v-stepper-content>
      </v-stepper>
    </v-container>
  </v-app>
</div>
new Vue({
  el: '#app',

  vuetify: new Vuetify(),

  data: () => ({
    step: 1,
    competencies: [
      'Foo',
      'Bar',
      'Fizz',
      'Buzz'
    ],
    skills: [
      'Communication',
      'Teamwork',
      'Adaptability',
      'Problem-Solving',
      'Creativity'
    ],
    selectedCompetencies: [],
    selectedSkillSets: []
  }),

  computed: {
    availableCompetencies() {
      let used = this.selectedSkillSets.map(set => set.competence)
      return this.selectedCompetencies.filter(competence => (
        !used.includes(competence)
      ))
    }
  },

  methods: {
    addSkillSet(competence) {
      this.selectedSkillSets.push({
        competence,
        skills: []
      })
    }
  }
})

Example

I hope this help.

Upvotes: 1

Related Questions