0

in my vue.js application i have a component which children inside, which are either of component type "Test" or "AnotherComponent". The user can add one of those components by a button click and it will then be added at the end of the list of child components.

Each of these components should have a delete-button to remove the component from the list shown.

I added such a button to "AnotherComponent" and added an event emitter on click because I wanted to notify the parent component of the event so it will take care of removing the right component from the list. Is this approach correct at all?

This is the child component

<template>
  <div>
    <div class="container">
        <b-card
            title="Card Title"
            class="mb-2"
        >
            <b-card-text>
                This card has some text and it different from the other component.
                Also there is a nice picture.
            </b-card-text>
            <button
                type="button"
                class="btn btn-secondary"
                v-on:click="deleteComponent()"
            >
              x
            </button>
        </b-card>
  </div>
  </div>
</template>

<script>

export default {
  name: 'AnotherComponent',
  data() {
    return {
    };
  },
  methods: {
    deleteComponent(event) {
      this.$emit('delete-component', this.$vnode.key);
    },
  },
};
</script>

This is the parent component:

<template>
  <div>
    <div class="container">
      <h1> This is my first layout page </h1>
      <alert :message=alertText></alert>
      <button
        type="button"
        class="btn btn-primary"
        v-on:click="addComponent('test')"
      >
        add component 1
      </button>
      <button
        type="button"
        class="btn btn-primary"
        v-on:click="addComponent('another')"
      >
        add component 2
      </button>
      <div class="row">
        <template
          v-for="block in content.body">
            <div class="col-3" v-bind:key="`col-${block._uid}`">
              <component
                :is="block.component"
                :block="block"
                :key="block._uid"
                v-on:delete-component="onDeleteChildComponent($event)">
              </component>
            </div>
        </template>
      </div>
  </div>
  </div>
</template>

<script>
/* eslint-disable vue/no-unused-components */
import axios from 'axios';

import TestComponent from './TestComponent.vue';
import AnotherComponent from './AnotherComponent.vue';
import Alert from './Alert.vue';

export default {
  name: 'FirstPage',
  data() {
    return {
      alertText: 'this is a test!',
      content: {
        body: [], /* holds the array of objects that are components */
      },
    };
  },
  components: {
    alert: Alert,
    test: TestComponent,
    another: AnotherComponent,
    // AnotherComponent,
  },
  methods: {
    getComponents() {
      const path = 'http://localhost:8080/components';
      axios.get(path)
        .then((res) => {
          console.log('res.data');
          this.content.body = res.data.components;
        })
        .catch((error) => {
          // eslint-disable-next-line
          console.error(error);
        });
    },
    addComponent(componentType) {
      const path = 'http://localhost:8080/components';
      const payload = {
        component: componentType,
        headline: 'Bar',
      };
      axios.post(path, payload)
        .then(() => {
          this.getComponents();
          this.message = 'Component added!';
        })
        .catch((error) => {
          // eslint-disable-next-line
          console.log(error);
          this.getComponents();
        });
    },
  },
  onDeleteChildComponent(id) {
    console.log('delete child');
    console.log(id);
  },
  created() {
    console.log('fetching components from backend');
    this.getComponents();
  },
};
</script>

Unfortunately in the parent component this event from th child does not get triggered at all. I am just starting with vue.js, so what am I doing wrong here? Thanks in advance!

4
  • 2
    You added delete-component event handler into div, not the component itself. Commented Apr 24, 2020 at 8:18
  • i tried the improvements you two mentioned above. But now i am getting errors which are: "[Vue warn]: Property or method "onDeleteChildComponent" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: vuejs.org/v2/guide/…. found in ---> <FirstPage> at src/components/FirstPage.vue <App> at src/App.vue <Root>" Commented Apr 24, 2020 at 8:25
  • That means at least the event is being triggered and listened to correctly. You've listed onDeleteChildComponent outside your methods. It should be in the methods object, but it is on the same level as your 'created' hook. Commented Apr 24, 2020 at 8:29
  • oh now I see it! Thanks a lot, it is working now :) Commented Apr 24, 2020 at 8:32

1 Answer 1

1

You're emitting 'deleteComponent' and listening to 'delete-component'. This conversion doesn't work with event registrations, only for converting component names.

https://v2.vuejs.org/v2/guide/components-custom-events.html

Try this instead:

// child component

methods: {
    deleteComponent(event) {
      this.$emit('delete-component', this.$vnode.key);
    },
  },

EDIT: As Eldar correctly pointed out in the comments, your listener is also listening at the wrong element. Move the listener to your dynamic component like so:

// parent component

<!-- template tags not necessary unless you're trying to render more than 1 root node per v-for element -->

            <div class="col-3" v-for="block in content.body" :key="`col-${block._uid}`">
              <component
                @delete-component="onDeleteChildComponent($event)"
                :is="block.component"
                :block="block"
                :key="block._uid">
              </component>
            </div>

EDIT 2: The method you're trying to listen to is not listed in your methods, but it's one level too high. Keep track of your brackets! ;)

methods: {
    getComponents() {
      ...
    },
    addComponent(componentType) {
      ...
    },
  },
  onDeleteChildComponent(id) {
    console.log('delete child');
    console.log(id);
  },

should be:

methods: {
    getComponents() {
      ...
    },
    addComponent(componentType) {
      ...
    },
    onDeleteChildComponent(id) {
      console.log('delete child');
      console.log(id);
    },
  },
Sign up to request clarification or add additional context in comments.

1 Comment

i tried it out like this and now get the error i mentioned in the comment for Eldars suggestion. What could be the problem now? (thanks for the tip with the unessary template btw)

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.