1

Guys so I'm trying to develop this rule component which can be spawned by my main component as many times as he wants but the problem is that when I delete an index from the list that tracks the number of rules, the vuejs layout doesn't update accordingly. What I mean by this is that if I check the array it self it deleted the correct item but when I look at the vue page (HTML) it either doesn't delete anything or only deletes the last item, and this may be caused by the v-for not updating on list change but I don't know how to solve this.

NewTask.vue (Parent)

<template>
  <div class="mt-4">
      <div class="container">
          <div class="if-condition container-fluid d-flex flex-row ps-0">
              <span class="text-center me-2 condition rounded">IF</span>
              <select class="form-select form-select-sm me-2 if-select" v-model="if_condition">
                <option value="ALL">ALL</option>
                <option value="ANY">ANY</option>
                <option value="NONE">NONE</option>
              </select>
              <span>of these filters match</span>
          </div>
          <div class="mt-2 ps-3 pt-3 pb-3 border">
            <new-rule v-for="(item, index) in rules"
                      :key="JSON.stringify(index)" v-on:remove-rule="removeRule(index)"
                      :data="item" :index="index" v-on:data-changed="dataChanged"
                      class="mb-2"/>
            <div class="mt-2 add-rule-div">
              <button class="btn add-rule-btn" v-on:click="addRule">+</button>
            </div>
          </div>
      </div>
  </div>
</template>

<script>
import Rule from '@/components/rule'

export default {
    name: "NewTask",
    components: {
      'new-rule': Rule
    },
    data: function () {
      return {
        if_condition: 'ALL',
        rules: []
      }
    },
  methods: {
      dataChanged(data) {
        const rules = this.rules;
        const index = data.index;
        delete data['index'];
        rules.splice(index, 1, data)
        this.rules = rules;
      },
      removeRule(index) {
        const rules = this.rules;
        rules.splice(index, 1)
        this.rules = rules
      },
      addRule() {
        const new_rule = {
          type_input_text: null,
          type_input_show: null,
          rule_input_text: null,
          rule_input_show: null,
        }
        this.rules.push(new_rule)
        console.log(this.rules)
      }
    }
  }
</script>

rule.vue (Child)

<template>
    <div class="if-condition d-flex flex-row">
      <select class="form-select form-select-sm me-2"
              v-on:change="checkTypeSelect" v-model="type_select">
        <option value="HTML">HTML</option>
        <option value="XPATH">XPATH</option>
        <option value="ID">ID</option>
        <option value="CLASS">CLASS</option>
      </select>
      <input v-if="type_input_show" type="text" class="form-control me-2" v-model="type_input_text" v-on:change="dataChanged">
      <select class="form-select form-select-sm me-2"
              v-on:change="checkRuleSelect" v-model="rule_select">
        <option value="CONTAINS">CONTAINS</option>
        <option value="EXISTS">EXISTS</option>
      </select>
      <input v-if="rule_input_show" type="text" class="form-control me-2" v-model="rule_input_text" v-on:change="dataChanged">
      <button class="btn remove-rule-btn pb-0 pt-0 ps-2 pe-2" v-on:click="this.$emit('remove-rule')">-</button>
    </div>
</template>

<script>
export default {
  name: "rule",
  props: {
    data: {
      type: Object,
      required: true
    },
    index: {
      type: Number,
      required: true
    }
  },
  data: function () {
    return {
      type_select: 'HTML',
      type_input_text: '',
      rule_select: 'CONTAINS',
      rule_input_text: '',
      //
      type_input_show: false,
      rule_input_show: true,
    }
  },
  beforeMount() {
    if (this.data.type_select) {
      this.type_select = this.data.type_select
      this.checkTypeSelect()
    }
    if (this.data.type_input_text) {
      this.type_input_text = this.data.type_input_text
    }
    if (this.data.rule_select) {
      this.rule_select = this.data.rule_select
      this.checkRuleSelect()
    }
    if (this.data.rule_input_text) {
      this.rule_input_text = this.data.rule_input_text
    }
  },
  methods: {
    dataChanged() {
      const new_data = {
        index: this.index,
        type_select: this.type_select,
        type_input_text: this.type_input_text,
        rule_select: this.rule_select,
        rule_input_text: this.rule_input_text
      }
      this.$emit('data-changed', new_data)
    },
    checkTypeSelect() {
      const type_select = this.type_select;
      this.type_input_show = type_select !== 'HTML';
      this.dataChanged()
    },
    checkRuleSelect() {
      const rule_select = this.rule_select;
      this.rule_input_show = rule_select !== 'EXISTS';
      this.dataChanged()
    }
  }
}
</script>

Some images to demonstrate the issue:

Array Before Index delete: enter image description here enter image description here

Array After Index delete: enter image description here enter image description here

For further investigation feel free to visit the repo: https://github.com/DEADSEC-SECURITY/Easy-Scraper

THIS IS NOT PUBLICITY, I TRULY NEED HELP

1
  • 1
    :key="JSON.stringify(index)" <- why JSON.stringify()? In any case, the issue here is using index as the key. When you do that, as far as Vue is concerned when the array changes size it's only ever adding / removing elements from the end Commented Nov 8, 2021 at 22:50

1 Answer 1

5

You are using the index as a key. VueJS updates the DOM based on the change in key. So you have two options:

  • Use some distinct data from each entry or combination as a key.
  • Provide no key because as per Vuejs docs you need not provide a key anymore.
Sign up to request clarification or add additional context in comments.

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.