1

So I have a problem with VueJs. I created a "Confirmation Dialogue" and added it to a On-Click-Event on buttons. It worked fine.

Now I tried to copy the implementation to add it to another button on a different parent. It says "TypeError: this.$refs.confirmDialogue.show is not a function" in the Console whenever I try to click the button. The other button still works completly normal.

Am I missing something? I already tried to remove the working button, so only one component uses the Confirm Dialogue but that also didn't work.

I'm new to VueJs. Hope someone can help me with this problem.

Child PopupModal:

<template>
    <transition name="fade">
        <div class="popup-modal" v-if="isVisible">
            <div class="window">
                <slot></slot>
            </div>
        </div>
    </transition>
</template>

<script>
export default {
    name: 'PopupModal',

    data: () => ({
        isVisible: false,
    }),

    methods: {
        open() {
            this.isVisible = true
        },

        close() {
            this.isVisible = false
        },
    },
}
</script>

<style scoped>
/* css class for the transition */
.fade-enter-active,
.fade-leave-active {
    transition: opacity 0.3s;
}
.fade-enter,
.fade-leave-to {
    opacity: 0;
}

.popup-modal {
    background-color: rgba(0, 0, 0, 0.5);
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    padding: 0.5rem;
    display: flex;
    align-items: center;
    z-index: 1;
}

.window {
    background: #fff;
    border-radius: 5px;
    box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.2);
    max-width: 480px;
    margin-left: auto;
    margin-right: auto;
    padding: 1rem;
}
</style>

Parent ConfirmDialogue:

<template>
    <popup-modal ref="popup">
        <h2 style="margin-top: 0">{{ title }}</h2>
        <p>{{ message }}</p>
        <div class="btns">
            <button class="cancel-btn" @click="_cancel">{{ cancelButton }}</button>
            <span class="ok-btn" @click="_confirm">{{ okButton }}</span>
        </div>
    </popup-modal>
</template>

<script>
import PopupModal from "../confirmDialogue/PopupModal.vue"

export default {
    name: 'ConfirmDialogue',

    components: { PopupModal },

    data: () => ({
        // Parameters that change depending on the type of dialogue
        title: undefined,
        message: undefined, // Main text content
        okButton: undefined, // Text for confirm button; leave it empty because we don't know what we're using it for
        cancelButton: 'Abbrechen', // text for cancel button
        
        // Private variables
        resolvePromise: undefined,
        rejectPromise: undefined,
    }),

    methods: {
        show(opts = {}) {
            this.title = opts.title
            this.message = opts.message
            this.okButton = opts.okButton
            if (opts.cancelButton) {
                this.cancelButton = opts.cancelButton
            }
            // Once we set our config, we tell the popup modal to open
            this.$refs.popup.open()
            // Return promise so the caller can get results
            return new Promise((resolve, reject) => {
                this.resolvePromise = resolve
                this.rejectPromise = reject
            })
        },

        _confirm() {
            this.$refs.popup.close()
            this.resolvePromise(true)
        },

        _cancel() {
            this.$refs.popup.close()
            this.resolvePromise(false)
            // Or you can throw an error
            // this.rejectPromise(new Error('User cancelled the dialogue'))
        },
    },
}
</script>

<style scoped>
.btns {
    display: flex;
    flex-direction: row;
    justify-content: space-between;
}

.ok-btn {
    padding: 0.5em 1em;
    background-color: #1F51FF;
    color: #fff;
    border: 2px solid #0ec5a4;
    border-radius: 5px;
    font-size: 16px;
    text-transform: uppercase;
    cursor: pointer;
}

.cancel-btn {
    padding: 0.5em 1em;
    background-color: #d5eae7;
    color: #000;
    border: 2px solid #0ec5a4;
    border-radius: 5px;
    font-size: 16px;
    text-transform: uppercase;
    cursor: pointer;
}
</style>

Working button:

            <td class="last-td last-row">
                <div class="button-wrapper">
                    <div class="wrapper-edit">
                        <button class="button button-edit">Bearbeiten</button>
                    </div>
                    <div class="wrapper-cancel">
                        <button class="button button-cancel" @click="doDelete">Löschen</button> <!-- Here is the working button -->
                        <confirm-dialogue ref="confirmDialogue"></confirm-dialogue>
                    </div>
                </div>
            </td>
        </tr>
    </thead>
</template>

<script>
import ConfirmDialogue from '../confirmDialogue/ConfirmDialogue.vue'

export default {
    name: "bookingElements",

    components: { ConfirmDialogue },
    
    methods: {
        async doDelete() {
            const ok = await this.$refs.confirmDialogue.show({
                title: 'Buchung löschen',
                message: 'Sind Sie sicher, dass Sie die Buchung löschen wollen?',
                okButton: 'Buchung löschen',
            })
            if (ok) {
                alert('Die Buchung wurde Erfolgreich gelöscht')
            }
        },

Button that isn't working:

            <td id="buttonCell">
                <button class="button" @click="doDelete">Buchen</button>
                <confirm-dialogue ref="confirmDialogue"></confirm-dialogue>
            </td>
        </tr>
    </tbody>
</template>

<script>
import ConfirmDialogue from "../confirmDialogue/ConfirmDialogue.vue"

export default {
    name: "bookElements",

    components: { ConfirmDialogue },

    methods: {
        async doDelete() {
            const ok = await this.$refs.confirmDialogue.show({
                title: 'Buchung löschen',
                message: 'Sind Sie sicher, dass Sie die Buchung löschen wollen?',
                okButton: 'Buchung löschen',
            })
            if (ok) {
                alert('Die Buchung wurde Erfolgreich gelöscht')
            }
        },
Joshua
  • 51
  • 1
  • 8
  • it'd be interesting to `console.log(this.$refs.confirmDialogue)` in the two places you attempt to use it to see the difference – Jaromanda X Sep 19 '22 at 11:27
  • you are double-pasting some code. Not sure how to interpret it. When a `$refs.*` doesnt exist its because the reference isnt there. There could be a `v-if` somewhere in the template that prevents the component from loading? – Flame Sep 19 '22 at 11:28
  • Also, try to debug that into a `mounted()` hook, when everything is set up regarding the DOM. Of course, if you try to access some refs of a child from a parent, it complicates a bit the whole lifecycle/refs existence. Then you could need to make some [hacky things](https://stackoverflow.com/a/67535239/8816585), at that point I recommend trying to look for another approach overall. – kissu Sep 19 '22 at 11:32

1 Answers1

2

I got found the mistake.

I created a for-each loop to create the Table. At the working button there was no for-each loop though.

So VueJs tried to generate the Confirmation Dialogue 9 times and this resulted to an error.

So I just need to put the

<confirm-dialogue ref="confirmDialogue"></confirm-dialogue>

to the top of Template like this

<template>
    <confirm-dialogue ref="confirmDialogue"></confirm-dialogue>
    ...
</template>

because it's vanished and only shows when the button is clicked.

Joshua
  • 51
  • 1
  • 8