skip to Main Content

I have this problem where I am getting an error when pushing the router using vue-router.

this.$router.push({ path: '/' }).catch(() => {});

This is the message it’s returning:

app.js:43636 Uncaught TypeError: Cannot read property 'classList' of undefined
    at Function.addClass

Weirdly enough in another child route it doesn’t occur. I have no idea what is causing it, it actually does what I want, it pushes to the homepage, but with this error.

The full file:

<template>
<div>
    <Card>
        <template #title>
            Payment
        </template>
        <template #content>
            <p class="p-text-secondary">Select a payment method to continue</p>
            <div class="p-fluid">
                <div class="p-field">
                    <!-- <InputText id="country" v-model="country" :class="{'p-invalid': validationErrors.selectedCity && submitted}" /> -->
                    <small v-show="validationErrors.selectedMethod && submitted" class="p-error">Country is required.</small>
                    <ScrollPanel class="custom">
                        <div v-for="paymentMethod in paymentMethods" :key="paymentMethod.name" class="p-field-radiobutton" :class="{'p-invalid': validationErrors.selectedMethod && submitted}">
                            <RadioButton :id="paymentMethod.key" name="paymentMethod" :value="paymentMethod" v-model="selectedMethod" :disabled="paymentMethod.key === 'R'" />
                            <label :for="paymentMethod.key">{{paymentMethod.name.toUpperCase()}}</label>
                        </div>
                    </ScrollPanel>
                    <small v-show="validationErrors.selectedMethod && submitted" class="p-error">Payment method is required.</small>
                </div>
            </div>
        </template>
        <template #footer>
            <div class="p-grid p-nogutter p-justify-between">
                <Button label="Back" @click="prevPage()" icon="pi pi-angle-left" />
                <Button label="Next" @click="nextPage()" icon="pi pi-angle-right" iconPos="right" />
            </div>
        </template>
    </Card>
</div>
</template>

<script>
export default {
    props : ['formData'],
    data () {
        return {
            paymentMethods: [
                {name: 'applepay'},
                {name: 'bancontact'},
                {name: 'banktransfer'},
                {name: 'creditcard'},
                {name: 'directdebit'},
                {name: 'eps'},
                {name: 'giftcard'},
                {name: 'giropay'},
                {name: 'ideal'},
                {name: 'kbc'},
                {name: 'mybank'},
                {name: 'paypal'},
                {name: 'paysafecard'},
                {name: 'przelewy24'},
                {name: 'sofort'},
                {name: 'belfius'},
            ],
            selectedMethod: null,
            validationErrors: {},
        }
    },
    created() {
        if( Object.entries(this.$props.formData).length === 0 ) {
            this.$router.push({ name: 'home' }).catch(() => {});
        }
    },
    methods: {
        nextPage() {
            this.$emit('nextPage', {formData: {paymentMethod: this.selectedMethod.name}, pageIndex: 1});
        },
        prevPage() {
            this.$emit('prevPage', {pageIndex: 1});
        },
        validateForm() {
            if (!this.selectedCountry.trim())
                this.validationErrors['selectedCountry'] = true;
            else
                delete this.validationErrors['selectedCountry'];
            if (!this.selectedCountry.trim())
                this.validationErrors['address'] = true;
            else
                delete this.validationErrors['address'];
            return !Object.keys(this.validationErrors).length;
        }
    }
}
</script>

<style lang="scss" scoped>
::v-deep .custom { 
    height: 200px;
}
::v-deep .custom .p-scrollpanel-wrapper {
    border-right: 9px solid #f4f4f4;
}

::v-deep .custom .p-scrollpanel-bar {
    background-color: #1976d2;
    opacity: 1;
    transition: background-color .3s;
}

::v-deep .custom .p-scrollpanel-bar:hover {
    background-color: #135ba1;
}
</style>

My Home.vue file:

<

template>
    <div class="layout-content">
        <div v-for="(ratings, index) in products.product_ratings" :key="index">
            {{ratings}}
        </div>
        <DataView :value="products" :layout="layout" :paginator="true" :rows="9" :sortOrder="sortOrder" :sortField="sortField">
            <template #header>
                <div class="p-grid p-nogutter">
                    <div class="p-col-6" style="text-align: left">
                        <Dropdown v-model="sortKey" :options="sortOptions" optionLabel="label" placeholder="Sort By Price" @change="onSortChange($event)"/>
                    </div>
                    <div class="p-col-6" style="text-align: right">
                        <DataViewLayoutOptions v-model="layout" />
                    </div>
                </div>
            </template>
            <template #list="slotProps">
                <div class="p-col-12" v-if="slotProps.data.units !== 0">
                    <div class="product-list-item">
                        <img :src="'/images/' + slotProps.data.image" :alt="slotProps.data.name"/>
                        <div class="product-list-detail">
                            <div class="product-name">{{slotProps.data.name}}</div>
                            <div class="product-description">{{slotProps.data.description}}</div>
                            <Rating :value="slotProps.data.averageRating" :readonly="true" :cancel="false"></Rating>
                            <i class="pi pi-tag product-category-icon"></i><span class="product-category">{{slotProps.data.category}}</span>
                        </div>
                        <div class="product-list-action">
                            <span class="product-price">{{formatCurrency(slotProps.data.price)}}</span>
                            <Button icon="pi pi-shopping-cart" label="Add to Cart" :disabled="slotProps.data.units === '0'"></Button>
                            <span :class="'product-badge status-'+slotProps.data.units">{{slotProps.data.units}}</span>
                        </div>
                    </div>
                </div>
            </template>

            <template #grid="slotProps">
                <div class="p-col-12 p-md-4" v-if="slotProps.data.units !== 0">
                    <div class="product-grid-item card">
                        <div class="product-grid-item-content">
                            <img :src="'/images/' + slotProps.data.image" :alt="slotProps.data.name"/>
                            <div class="product-name">{{slotProps.data.name}}</div>
                            <div class="product-description">{{slotProps.data.description}}</div>
                            <Rating :value="slotProps.data.averageRating" :readonly="true" :cancel="false"></Rating>
                        </div>
                        <div class="product-grid-item-bottom">
                            <span class="product-price">{{formatCurrency(slotProps.data.price)}}</span>
                            <div class="row">
                                <router-link :to="{ path: '/products/'+slotProps.data.id }"><Button class="btn btn-primary mr-2">Buy now</Button></router-link>
                                <div v-if="!isLoggedIn">
                                    <router-link :to="{ path: 'login' }"><Button icon="pi pi-shopping-cart"></Button></router-link>                                   
                                </div>
                                <div v-if="isLoggedIn">
                                    <Button icon="pi pi-shopping-cart" :disabled="slotProps.data.units == '0'" @click="addToCart(slotProps.data)"></Button>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </template>
        </DataView>
    </div>
</template>



<script>
    export default {
        data() {
            return {
                products : [],
                product: {},
                isLoggedIn : null,
                visibleLeft : false,
                layout: 'grid',
                sortKey: null,
                sortOrder: null,
                sortField: null,
                sortOptions: [
                    {label: 'Price High to Low', value: '!price'},
                    {label: 'Price Low to High', value: 'price'},
                ],
                responsiveOptions: [
                {
                    breakpoint: '1024px',
                    numVisible: 3,
                    numScroll: 3
                },
                {
                    breakpoint: '600px',
                    numVisible: 2,
                    numScroll: 2
                },
                {
                    breakpoint: '480px',
                    numVisible: 1,
                    numScroll: 1
                }
                ],
            }
        },
        mounted() {
            this.isLoggedIn = localStorage.getItem('vue-laravel-ecommerce.jwt') != null
            axios.get("api/products/").then(response => {
                let products = response.data.products
                products = products.map(product => {
                const totalRatings = product.product_rating.reduce((acc, { rating }) => acc += Number(rating), 0)
                const averageRating = totalRatings/product.product_rating.length
                return {...product, averageRating}
                })

                this.products = products.map(product => {
                const category = product.product_category.name
                return {...product, category}
                })
            });
        },
        methods : {
            formatCurrency(value) {
                return value.toLocaleString('nl-NL', {style: 'currency', currency: 'EUR'});
            },
            onSortChange(event){
                const value = event.value.value;
                const sortValue = event.value;

                if (value.indexOf('!') === 0) {
                    this.sortOrder = -1;
                    this.sortField = value.substring(1, value.length);
                    this.sortKey = sortValue;
                }
                else {
                    this.sortOrder = 1;
                    this.sortField = value;
                    this.sortKey = sortValue;
                }
            },
            addToCart(product) {
                product.quantity = 1;
                this.$parent.$emit('addToCart', product);
            }
        },
    }
</script>

<style lang="scss" scoped>
.custom-carousel {
    //background: #000000;
}
.product-item {
    .product-item-content {
        border: 1px solid var(--surface-d);
        border-radius: 3px;
        margin: .3rem;
        text-align: center;
        padding: 2rem 0;

    }

    .product-image {
        width: 200px;
        height: 150px;
        box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23)
    }
}
.p-dropdown {
    width: 14rem;
    font-weight: normal;
}
.product-name {
    font-size: 1.5rem;
    font-weight: 700;
}
.product-description {
    margin: 0 0 1rem 0;
}
.product-category-icon {
    vertical-align: middle;
    margin-right: .5rem;
}
.product-category {
    font-weight: 600;
    vertical-align: middle;
}
::v-deep .product-list-item {
    display: flex;
    align-items: center;
    padding: 1rem;
    width: 100%;
    padding: 2rem;
    img {
        width: 150px;
        box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);
        margin-right: 2rem;
    }
    .product-list-detail {
        flex: 1 1 0;
    }
    .p-rating {
        margin: 0 0 .5rem 0;
    }
    .product-price {
        font-size: 1.5rem;
        font-weight: 600;
        margin-bottom: .5rem;
        align-self: flex-end;
    }
    .product-list-action {
        display: flex;
        flex-direction: column;
    }
    .p-button {
        margin-bottom: .5rem;
    }
}
::v-deep .product-grid-item {
    padding: 2rem;
    .product-grid-item-top,
    .product-grid-item-bottom {
        display: flex;
        align-items: center;
        justify-content: space-between;
    }
    img {
        width: 150px;
        height: 150px;
        box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);
        margin: 2rem 0;
    }
    .product-grid-item-content {
        text-align: center;
    }
    .product-price {
        font-size: 1.5rem;
        font-weight: 600;
    }
}
@media screen and (max-width: 576px) {
    .product-list-item {
        flex-direction: column;
        align-items: center;
        img {
            width: 75%;
            margin: 2rem 0;
        }
        .product-list-detail {
            text-align: center;
        }
        .product-price {
            align-self: center;
        }
        .product-list-action {
            display: flex;
            flex-direction: column;
        }
        .product-list-action {
            margin-top: 2rem;
            flex-direction: row;
            justify-content: space-between;
            align-items: center;
            width: 100%;
        }
    }
}
</style>

UPDATE
Error getting caused by this component of the PrimeVue library:
https://www.primefaces.org/primevue/showcase-v2/#/scrollpanel

ScrollPanel.vue:

<template>
    <div class="p-scrollpanel p-component">
        <div class="p-scrollpanel-wrapper">
            <div ref="content" class="p-scrollpanel-content" @scroll="moveBar" @mouseenter="moveBar">
                <slot></slot>
            </div>
        </div>
        <div ref="xBar" class="p-scrollpanel-bar p-scrollpanel-bar-x" @mousedown="onXBarMouseDown"></div>
        <div ref="yBar" class="p-scrollpanel-bar p-scrollpanel-bar-y" @mousedown="onYBarMouseDown"></div>
    </div>
</template>

<script>
import DomHandler from '../utils/DomHandler';

export default {
    initialized: false,
    documentResizeListener: null,
    documentMouseMoveListener: null,
    documentMouseUpListener: null,
    frame: null,
    scrollXRatio: null,
    scrollYRatio: null,
    isXBarClicked: false,
    isYBarClicked: false,
    lastPageX: null,
    lastPageY: null,
    mounted() {
        if (this.$el.offsetParent) {
            this.initialize();
        }
    },
    updated() {
        if (!this.initialized && this.$el.offsetParent) {
            this.initialize();
        }
    },
    beforeDestroy() {
        this.unbindDocumentResizeListener();

        if (this.frame) {
            window.cancelAnimationFrame(this.frame);
        }
    },
    methods: {
        initialize() {
            this.moveBar();
            this.bindDocumentResizeListener();
            this.calculateContainerHeight();
        },
        calculateContainerHeight() {
            let containerStyles = getComputedStyle(this.$el),
            xBarStyles = getComputedStyle(this.$refs.xBar),
            pureContainerHeight = DomHandler.getHeight(this.$el) - parseInt(xBarStyles['height'], 10);

            if (containerStyles['max-height'] !== "none" && pureContainerHeight === 0) {
                if(this.$refs.content.offsetHeight + parseInt(xBarStyles['height'], 10) > parseInt(containerStyles['max-height'], 10)) {
                    this.$el.style.height = containerStyles['max-height'];
                }
                else {
                    this.$el.style.height = this.$refs.content.offsetHeight + parseFloat(containerStyles.paddingTop) + parseFloat(containerStyles.paddingBottom) + parseFloat(containerStyles.borderTopWidth) + parseFloat(containerStyles.borderBottomWidth) + "px";
                }
            }
        },
        moveBar() {
            /* horizontal scroll */
            let totalWidth = this.$refs.content.scrollWidth;
            let ownWidth = this.$refs.content.clientWidth;
            let bottom = (this.$el.clientHeight - this.$refs.xBar.clientHeight) * -1;

            this.scrollXRatio = ownWidth / totalWidth;

            /* vertical scroll */
            let totalHeight = this.$refs.content.scrollHeight;
            let ownHeight = this.$refs.content.clientHeight;
            let right = (this.$el.clientWidth - this.$refs.yBar.clientWidth) * -1;

            this.scrollYRatio = ownHeight / totalHeight;

            this.frame = this.requestAnimationFrame(() => {
                if (this.scrollXRatio >= 1) {
                    DomHandler.addClass(this.$refs.xBar, 'p-scrollpanel-hidden');
                }
                else {
                    DomHandler.removeClass(this.$refs.xBar, 'p-scrollpanel-hidden');
                    this.$refs.xBar.style.cssText = 'width:' + Math.max(this.scrollXRatio * 100, 10) + '%; left:' + (this.$refs.content.scrollLeft / totalWidth) * 100 + '%;bottom:' + bottom + 'px;';
                }

                if (this.scrollYRatio >= 1) {
                    DomHandler.addClass(this.$refs.yBar, 'p-scrollpanel-hidden');
                }
                else {
                    DomHandler.removeClass(this.$refs.yBar, 'p-scrollpanel-hidden');
                    this.$refs.yBar.style.cssText = 'height:' + Math.max(this.scrollYRatio * 100, 10) + '%; top: calc(' + (this.$refs.content.scrollTop / totalHeight) * 100 + '% - ' + this.$refs.xBar.clientHeight + 'px);right:' + right + 'px;';
                }
            });
        },
        onYBarMouseDown(e) {
            this.isYBarClicked = true;
            this.lastPageY = e.pageY;
            DomHandler.addClass(this.$refs.yBar, 'p-scrollpanel-grabbed');
            DomHandler.addClass(document.body, 'p-scrollpanel-grabbed');

            this.bindDocumentMouseListeners();
            e.preventDefault();
        },
        onXBarMouseDown(e) {
            this.isXBarClicked = true;
            this.lastPageX = e.pageX;
            DomHandler.addClass(this.$refs.xBar, 'p-scrollpanel-grabbed');
            DomHandler.addClass(document.body, 'p-scrollpanel-grabbed');

            this.bindDocumentMouseListeners();
            e.preventDefault();
        },
        onDocumentMouseMove(e) {
            if (this.isXBarClicked) {
                this.onMouseMoveForXBar(e);
            }
            else if (this.isYBarClicked) {
                this.onMouseMoveForYBar(e);
            }
            else {
                this.onMouseMoveForXBar(e);
                this.onMouseMoveForYBar(e);
            }
        },
        onMouseMoveForXBar(e) {
            let deltaX = e.pageX - this.lastPageX;
            this.lastPageX = e.pageX;

            this.frame = this.requestAnimationFrame(() => {
                this.$refs.content.scrollLeft += deltaX / this.scrollXRatio;
            });
        },
        onMouseMoveForYBar(e) {
            let deltaY = e.pageY - this.lastPageY;
            this.lastPageY = e.pageY;

            this.frame = this.requestAnimationFrame(() => {
                this.$refs.content.scrollTop += deltaY / this.scrollYRatio;
            });
        },
        onDocumentMouseUp() {
            DomHandler.removeClass(this.$refs.yBar, 'p-scrollpanel-grabbed');
            DomHandler.removeClass(this.$refs.xBar, 'p-scrollpanel-grabbed');
            DomHandler.removeClass(document.body, 'p-scrollpanel-grabbed');

            this.unbindDocumentMouseListeners();
            this.isXBarClicked = false;
            this.isYBarClicked = false;
        },
        requestAnimationFrame(f) {
            let frame = window.requestAnimationFrame || this.timeoutFrame;
            frame(f);
        },
        refresh() {
            this.moveBar();
        },
        scrollTop(scrollTop) {
            let scrollableHeight = this.$refs.content.scrollHeight - this.$refs.content.clientHeight;
            scrollTop = scrollTop > scrollableHeight ? scrollableHeight : scrollTop > 0 ? scrollTop : 0;
            this.$refs.contentscrollTop = scrollTop;
        },
        bindDocumentMouseListeners() {
            if (!this.documentMouseMoveListener) {
                this.documentMouseMoveListener = (e) => {
                    this.onDocumentMouseMove(e);
                };

                document.addEventListener('mousemove', this.documentMouseMoveListener);
            }

            if (!this.documentMouseUpListener) {
                this.documentMouseUpListener = (e) => {
                    this.onDocumentMouseUp(e);
                };

                document.addEventListener('mouseup', this.documentMouseUpListener);
            }
        },
        unbindDocumentMouseListeners() {
            if (this.documentMouseMoveListener) {
                document.removeEventListener('mousemove', this.documentMouseMoveListener);
                this.documentMouseMoveListener = null;
            }

            if(this.documentMouseUpListener) {
                document.removeEventListener('mouseup', this.documentMouseUpListener);
                this.documentMouseUpListener = null;
            }
        },
        bindDocumentResizeListener() {
            if (!this.documentResizeListener) {
                this.documentResizeListener = () => {
                    this.moveBar();
                };

                window.addEventListener('resize', this.documentResizeListener);
            }
        },
        unbindDocumentResizeListener() {
            if(this.documentResizeListener) {
                window.removeEventListener('resize', this.documentResizeListener);
                this.documentResizeListener = null;
            }
        }
    }
}
</script>

<style>
.p-scrollpanel-wrapper {
    overflow: hidden;
    width: 100%;
    height: 100%;
    position: relative;
    z-index: 1;
    float: left;
}

.p-scrollpanel-content {
    height: calc(100% + 18px);
    width: calc(100% + 18px);
    padding: 0 18px 18px 0;
    position: relative;
    overflow: auto;
    box-sizing: border-box;
}

.p-scrollpanel-bar {
    position: relative;
    background: #c1c1c1;
    border-radius: 3px;
    z-index: 2;
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.25s linear;
}

.p-scrollpanel-bar-y {
    width: 9px;
    top: 0;
}

.p-scrollpanel-bar-x {
    height: 9px;
    bottom: 0;
}

.p-scrollpanel-hidden {
    visibility: hidden;
}

.p-scrollpanel:hover .p-scrollpanel-bar,
.p-scrollpanel:active .p-scrollpanel-bar {
    opacity: 1;
}

.p-scrollpanel-grabbed {
    user-select: none;
}
</style>

2

Answers


  1. Chosen as BEST ANSWER

    The error occured because of the ScrollPanel component of the PrimeVue library: https://www.primefaces.org/primevue/showcase-v2/#/scrollpanel

    I figured, why not change it to a radiobutton group of the Bootstrap library, now I am not left with any errors.

    Maybe in the future I can swap back to using the custom component.


  2. @sanderplomp the problem is not with the router but instead with something you’re doing on the page you are trying to navigate to. Look for the area of code where you’re calling .addClass() to some element. This is the cause of your error. Without seeing your code it sounds like the element you’re trying to add a class to does not yet exist in the dom. You’ll have to tweak your logic a little or add a check for that particular element’s existence before interacting with it.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search