VueJs Phonebook app with nested table data Error: "Maximum call stack size exceeded" when trying to .push()

Im trying to build simple Phonebook app, with table and nested data in it. I’m using element ui.

Main thing this app must do is to add a new contact and a new nested contact, when you choosing contact "leader".
New contact and new nested contact for preset contacts are added correctly.

But when i’m trying to add nested contact to just created contact in trows an error "Maximum call stack size exceeded".

Here is code:

App.vue

<template>
  <div id="app">
    <ContactsTable :list="list"/>
    <ModalForm :list="list" @submitForm="onFormSubmit"/>
  </div>
</template>

<script>
import ContactsTable from "@/components/ContactsTable";
import ModalForm from "@/components/ModalForm";

export default {
  name: 'App',
  components: {
    ContactsTable,
    ModalForm,
  },
  data: () => ({
    list: [{
      leader: '',
        name: 'Silvia',
        number: +54321236576,
        id: 17,
        children: [{
          leader: 17,
          name: 'Joe',
          number: +87653459809,
          id: 191,
          children: []
        }]
      },
      {
        leader: '',
        name: 'Victor',
        number: +98765434560,
        id: 42,
        children: [{
          leader: 42,
          name: 'Sam',
          number: +14573564378,
          id: 202,
          children: [{
            leader: 202,
            name: 'Mona',
            number: +77774352309,
            id: 2092,
          }]
        }]
      }],
  }),
  methods: {
    onFormSubmit(data) {
      const newObj = {
        ...data,
        id: Math.random()
      };
      if (!(data.leader)) {
        this.list.push(newObj);
      } else {
        for(let i = 0; i < this.list.length; i++) {
          if(this.list[i].id === data.leader) {
            this.list[i].children.push(newObj)
          }
        }
      }
    }
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
  margin: auto;
}
</style>

ModalForm.vue

<template>
    <el-card class="modal-wrap" shadow="never">
      <el-button type="primary" @click="dialogVisible = true">New contact</el-button>

      <el-dialog class="dialog" :visible.sync="dialogVisible">
        <el-form :model="form" :data="list" :rules="rules" ref="addItemForm" label-position="left" label-width="120px">
          <el-form-item label="Name" prop="name">
            <el-input v-model="form.name" autocomplete="off"/>
          </el-form-item>

          <el-form-item label="Phone number" prop="number">
            <el-input v-model="form.number" autocomplete="off"/>
          </el-form-item>

          <el-form-item label="Team leader" prop="leader">
            <el-select v-model="form.leader">
              <el-option v-for="item in list" :key="item.id" :label="item.name" :value="item.id"/>
            </el-select>
          </el-form-item>

          <el-form-item class="form-action-buttons">
            <el-button>Cancel</el-button>
            <el-button type="primary" @click="onSubmit">Save</el-button>
          </el-form-item>
        </el-form>
      </el-dialog>
    </el-card>
</template>

<script>
export default {
  name: "ModalForm",
  props: {
    list: {
      type: Array,
      default: () => ({})
    }
  },
  data() {
    return {
      dialogVisible: false,
      form: {
        id: '',
        name: '',
        number: '',
        leader: '',
        children: []
      },
      rules: {
        name: [
          { required: true, message: 'Name required', trigger: 'blur' },
        ],
        number: [
          { required: true, message: 'Phone number required', trigger: 'blur' },
        ],
      }
    };
  },
  methods: {
    onSubmit() {
      this.$emit("submitForm", { ...this.form });
      this.$refs.addItemForm.resetFields();
    }
  }
}
</script>

<style scoped>
.modal-wrap {
  max-width: 40%;
  margin: auto;
  border: none;
}
.dialog {
  margin: auto;
  max-width: 70%;
}
.form-action-buttons {
  display: flex;
}
</style>

ContactsTable.vue

<template>
  <div class="contacts-list-wrap">
    <el-card :header="header" shadow="never">
      <el-table :data="list" row-key="id" :tree-props="{children: 'children', hasChildren: 'hasChildren'}" border stripe>
        <el-table-column prop="name" label="Name" sortable/>
        <el-table-column prop="number" label="Phone" sortable/>
      </el-table>
    </el-card>
  </div>
</template>

<script>
export default {
  name: "ContactsTable",
  props: {
    list: {
      type: Array,
      default: () => ({})
    }
  },
  data: () => ({
    header: "Phonebook"
  }),
}
</script>

<style scoped>
.contacts-list-wrap {
  max-width: 40%;
  margin: auto;
}
</style>

Please help me figure out what could be the reason?

I also need to understand how to add child contacts to all nesting levels…

Thank you for your responses!

71 thoughts on “VueJs Phonebook app with nested table data Error: "Maximum call stack size exceeded" when trying to .push()”

Leave a Comment