link2603 link2604 link2605 link2606 link2607 link2608 link2609 link2610 link2611 link2612 link2613 link2614 link2615 link2616 link2617 link2618 link2619 link2620 link2621 link2622 link2623 link2624 link2625 link2626 link2627 link2628 link2629 link2630 link2631 link2632 link2633 link2634 link2635 link2636 link2637 link2638 link2639 link2640 link2641 link2642 link2643 link2644 link2645 link2646 link2647 link2648 link2649 link2650 link2651 link2652 link2653 link2654 link2655 link2656 link2657 link2658 link2659 link2660 link2661 link2662 link2663 link2664 link2665 link2666 link2667 link2668 link2669 link2670 link2671 link2672 link2673 link2674 link2675 link2676 link2677 link2678 link2679 link2680 link2681 link2682 link2683 link2684 link2685 link2686 link2687 link2688 link2689 link2690 link2691 link2692 link2693 link2694 link2695 link2696 link2697 link2698 link2699 link2700 link2701 link2702 link2703 link2704 link2705 link2706 link2707 link2708 link2709 link2710 link2711 link2712 link2713 link2714 link2715 link2716 link2717 link2718 link2719 link2720 link2721 link2722 link2723 link2724 link2725 link2726 link2727 link2728 link2729 link2730 link2731 link2732 link2733 link2734 link2735 link2736 link2737 link2738 link2739

[Vue.js] Display different Vuejs components for mobile browsers

when developing an SPA using vue.js 2.0. The components developed so far are for the “desktop” browsers, for example, I have

Main.vue,
ProductList.vue,
ProductDetail.vue,

another set of components for the mobile browsers, such as
MainMobile.vue,
ProductListMobile.vue,
ProductDetailMobile.vue,

My question is, where and how do I make my SPA render the mobile version of components when viewing in a mobile browser?

Please note that I explicitly want to avoid making my components responsive. to keep two separate versions of them.

Thanks,

Solution :

there is an idea, use a mixin which detects is the browser mobile or desktop (example for js code in this answer ).. then use v-if, for example

<production-list v-if=”!isMobile()”></production-list>
<production-list-mobile v-else></production-list-mobile>

so here is an example on https://jsfiddle.net/Ldku0xec/

Solution 2:

there is simple solution for Vue.js:

<div v-if=”!isMobile()”>
<desktop>
</desktop>
</div>
<div v-else>
<mobile>
</mobile>
</div>

And methods:

methods: {
isMobile() {
if(/Android|webOS|iPhone||iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
return true
} else {
return false
}
}
}

Solution 3:

I had this same problem, I solved it using a neutral and no layout vue.js file (Init.vue) that will be accessed by mobile and desktop, and this file redirects to the correct file.

Let’s suppose that there is the Main.vue.js and the MainMobile.vue. I will add an Init.vue.js that will redirect. So my router/index.js is that:

import Router from ‘vue-router’
import vue.js from ‘vue’
import Main from ‘@/components/Main’
import MainMobile from ‘@/components/MainMobile’
import Init from ‘@/components/Init’

Vue.use(Router)

export default new Router({
routes: [
{
path: ‘/‘,
name: ‘Root’,
component: Init
},
{
path: ‘/Main’,
name: ‘Main’,
component: Main
},
{
path: ‘/MainMobile’,
name: ‘MainMobile’,
component: MainMobile
},
]
})

At the Init.vue.js file, the mobile/desktop detection will happen:

<template>
</template>
<script>
export default {
name: ‘Init’,
methods: {
isMobile() {
if( screen.width <= 760 ) {
return true;
}
else {
return false;
}
}
},
created() {
if (this.isMobile()) {
this.$router.push(‘/MainMobile’);
}
else {
this.$router.push(‘/Main’);
}
}
}
</script>
<style scoped>
</style>

The isMobile() function used is very simple, you can change to any other.

Solution 4:

A bit late for this but, in case if any of you are looking for I handled the situation like this:
I added meta to my router:

const router = new Router({
routes: [{
path: ‘/main-view
name: ‘mainView’,
component: MainView,
meta: {
‘hasMobileView’: true
}
},
{
path: ‘/mobile-view’,
name: ‘mobileView’,
component: mobileView,
meta: {
‘hasDesktopView’: true
}
},
}]
})

then on beforeeach function

router.beforeEach((to, from, next) => {
const hasMobileView = to.matched.some((route) => route.meta.hasMobileView)
if (hasMobileView) {
if (navigator.userAgent.match(/Android/i) ||
navigator.userAgent.match(/webOS/i) ||
navigator.userAgent.match(/iPhone/i) ||
navigator.userAgent.match(/iPad/i) ||
navigator.userAgent.match(/iPod/i) ||
navigator.userAgent.match(/BlackBerry/i) ||
navigator.userAgent.match(/Windows Phone/i)) {
next(‘/mobile-view’)
} else {
next()
}
}
})`

Solution 5:

I was looking for a solution for this and came here but I couldn’t find what I needed:

Asynchronous imports to only load into the bundle what was needed based on the viewport.
Capability to serve a different layout if the layout was resized

I mixed and matched a few things I read online including answers here so I thought I’d just come back and put all my learnings into one function for anyone else looking:

/**
* Breakpoint configuration to be in line with element-ui’s standards
* @type {LABELS: string[], VALUES: number[]}
*/
const BREAKPOINTS = {
LABELS: [‘xs’, ‘sm’, ‘md’, ‘lg’, ‘xl’],
VALUES: [0, 768, 992, 1200, 1920, Infinity]
};

/**
* @typedef ViewFactory
* @type function
* A function which returns a promise which resolves to a view. Used to dynamically fetch a view file on the fly during
* run time on a need basis
*/

/**
* A helper to get a responsive route factory which renders different views based on the current view point
* @param {xs:[ViewFactory],sm:[ViewFactory],md:[ViewFactory],lg:[ViewFactory]} map - A map of breakpoint key to a ViewFactory
* @returns {ViewFactory} - A view factory which invokes and returns an item supplied in the map based on the current viewport size
*/
export default function responsiveRoute(map) {
return function getResponsiveView() {
const screenWidth = document.documentElement.clientWidth;

// Find the matching index for the current screen width
const matchIndex = BREAKPOINTS.VALUES.findIndex((item, idx) => {
if (idx === 0) {
return false;
}
return screenWidth >= BREAKPOINTS.VALUES[idx - 1] && screenWidth < BREAKPOINTS.VALUES[idx];
}) - 1;

if (map[BREAKPOINTS.LABELS[matchIndex]]) {
// Perfect match, use it
return map[BREAKPOINTS.LABELS[matchIndex]]();
} else {
// Go down the responsive break points list until a match is found
let counter = matchIndex;
while (counter– > 0) {
if (map[BREAKPOINTS.LABELS[counter]]) {
return map[BREAKPOINTS.LABELS[counter]]();
}
}
return Promise.reject({
code: 500,
info: ‘No component matched the breakpoint - probably a configuration error’
});
}
};
}

Usage:

const router = new Router({
mode: ‘history’,
base: process.env.BASE_URL,
routes:[{
path: ‘/login’,
name: ‘login’,
component: responsiveRoute({
// route level code-splitting
// this generates a separate chunk (login-xs.[hash].js) for this route
// which is lazy-loaded when the route is visited.
xs: () => import(/* webpackChunkName: “login-xs” */ ‘./views/Login/Login-xs.vue’),
// sm key is missing, it falls back to xs
md: () => import(/* webpackChunkName: “login-md” */ ‘./views/Login/Login-md.vue’)
// lg, xl keys are missing falls back to md
})
}]
});

How it works:

vue.js Router supports defining the component key as a function which returns a promise to support async routes. The most common way being to use the webpack import() function which returns a promise. The function which returns the promise is only invoked when the route is about to be rendered ensuring we can lazy load our components

The responsiveRoute function accepts a map of these functions with keys set for different breakpoints and returns a function which, when invoked, checks the available viewport size and returns invokes the correct promise factory and return’s the promise returned by it.

Notes:

I like this method because it does not require the application architecture or route configurations to be in a certain way. It’s pretty plug and play using vue.js Router capabilities provided out of the box. It also does not force you to define a view for every breakpoint-route combination. You can define a route as usual without this(lazy loaded or not) along side other routes that use this without any problems.

This method does not use user agent sniffing but uses the available width of the document.documentElement instead. Other methods I saw recommended things like window.screen.width which gives the exact device screen size regardless of the window size or a more robust window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth. Mix and match as needed.

My break points are (number and their values) are based on element-ui breakpoints as I used that for normal responsive design. This can again be configured as needed by changing the constants at the top

Solution 6:

there is a better solution.In src/main.js:

if (condition) {
require(‘./pc/main)
}else {
require(‘./mobile/main’)
}

Solution 7:

Maybe the most elegant solution is:

Paste it into the Main.js

router.onReady(() => {
if (window.innerWidth < 1000) {
router.push(‘/mobile’)
} else {
router.push(‘/‘)
}
});

[Vue.js] Vue axios makes put request to wrong URL

so I’ve got this weird problem where if I make a Put request through axios in Vue, it’s sending it to the wrong route.

It’s part of a simple CRUD through a Laravel APIresource. Get and post work fine.

This is the form:

<form @submit.prevent=”editType” method=”post” class=”mb-4”>
<label v-if=”user.gender === ‘man’” for=”single_male”>Enkel<input type=”checkbox” name=”type” :id=”single_male” :value=”types.single_male” v-model=”checkedType” @change=”showSaveButton = true”></label>
<label v-if=”user.gender === ‘vrouw’” for=”single_female”>Enkel<input type=”checkbox” name=”type” :id=”single_female” :value=”types.single_female” v-model=”checkedType” @change=”showSaveButton = true”></label>
<label v-if=”user.gender === ‘man’” for=”double_male”>Dubbel mannen<input type=”checkbox” name=”type” :id=”double_male” :value=”types.double_male” v-model=”checkedType” @change=”showSaveButton = true”></label>
<label v-if=”user.gender === ‘vrouw’” for=”double_female”>Dubbel vrouwen<input type=”checkbox” name=”type” :id=”double_female” :value=”types.double_female” v-model=”checkedType” @change=”showSaveButton = true”></label>
<label for=”double_mix”>Dubbel gemengd<input type=”checkbox” name=”type” :id=”double_mix” :value=”types.double_mix” v-model=”checkedType” @change=”showSaveButton”></label>
<button type=”submit” v-if=”showSaveButton”>Opslaan</button>
</form>

My update function:

public function update(Request $request, $id)
{
$user_id = auth(‘api’)->user()->id;

$type = Type::where(‘user_id’ ,’=’, $user_id)->first();

$type->single_male = $request->input(‘single_male’);
$type->single_female = $request->input(‘single_female’);
$type->double_male = $request->input(‘double_male’);
$type->double_female = $request->input(‘double_female’);
$type->double_mix = $request->input(‘double_mix’);

$type->save();

return redirect()->back();
}

my method:

editType() {
let types = this.types;
let data = {
single_male: this.single_male,
single_female: this.single_female,
double_male: this.double_male,
double_female: this.double_female,
double_mix: this.double_mix,
};
axios.put(‘/types/‘+types.id, data)
.then(request => this.successfulEdit(request))
.catch(() => this.failed())
},
successfulEdit() {
alert(“Voorkeuren succesvol bijgewerkt!”);
}

and my route:

Route::apiresource(‘types’,’TypeController’);

When I make a put request, I can see in the Developer Tools that it tries to make the request to the current url, which of course gives a 405 method not allowed error.
When I change the axios request to just (‘/types’, data), it does follow the specified route but of course gives the 405 error too since the put method requires an id. Hardcoding to e.g. ‘/types/4/‘ also leads to using the current url.

Am I missing somethng or what’s wrong? Thanks in advance guys!

Solution :

Found the problem. The return redirect()->back(); was the culprit. Thanks y’all!

Solution 2:

From Laravel docs. https://laravel.com/docs/5.8/routing

Routes defined in the routes/api.php file are nested within a route
group by the RouteServiceProvider. Within this group, the /api URI
prefix is automatically applied

So try to add /api before the current request URL like
axios.put(‘/api/types/‘+types.id, data)

[Vue.js] How to use BugSnag inside of a nuxt.js app?

BugSnag provides a very useful and initially free product for tracking errors in the vue.js app. The problem is that there is no documentation for using this in a nuxt app. A plugin would be the best place to utilize it in the app.

Trying to resolve this was killing me for a while but I was able to find help from Patryk Padus from the comments on this post.

Solution :

For anyone trying to make this happen, do the following:

1.Place the following code inside of a plugin located in the /plugins folder of the application root:

#/plugins/bugsnag.js
import vue.js from ‘vue’
import bugsnag from ‘@bugsnag/js’
import bugsnagvue.js from ‘@bugsnag/plugin-vue’

const bugsnagClient = bugsnag({
apiKey: ‘YOUR-KEY’,
notifyReleaseStages: [ ‘production’, ‘staging’ ]
})

bugsnagClient.use(bugsnagVue, Vue);

export default (ctx, inject) => {
inject(‘bugsnag’, bugsnagClient)
}

2.Inside of the nuxt.config add the following to the plugins section:

plugins: [
‘@/plugins/bugsnag.js’,
],

3.Inside of the vue.js layout reference the bugsnag object using the $bugsnag object:

this.$bugsnag.notify(new Error(‘Nuxt Test error’))

[Vue.js] (Vue.js) V-model array. Having difficulty running function

I’m working with a portfolio project, where users can upload a picture, provide a description below it, and then click the “add” button to add another image and description.

I’m trying to add a character counter to the description input, which is a textarea field. Usually I can add the name of the v-model into the function, and it works fine, but this textarea is in a for-loop, so I’m not sure how to get this function to work.

Template:

<div class=”newPortfolioList”>
<div class=”newPortfolioItem” v-for=”(item, index) in this.portfolioItems” v-bind:key=”index”>
….
<div class=”newPortfolioDescription”>
<textarea v-model=”item.portfolioDescription” @keyup=’remaincharCount()’ maxlength=”1000” placeholder=”Item Description…”></textarea>
</div>

<!– Displaying the remaining characters –>
<span style=”text-align:left; padding: 10px;”>{ remaincharactersText }</span>
</div>

Script:

export default {
data () {
return {
portfolioItems:[],
maxcharacter: 1000,
remaincharactersText: “1000 characters remaining”
}
},
methods: {
createPortfolioItem () {
this.portfolioItems.push({
portfolioDescription: ‘’
})
},
remaincharCount () {
if (this.foo.length > this.maxcharacter) {
this.remaincharactersText = “Exceeded “+this.maxcharacter+” characters limit.”;
} else {
var remainCharacters = this.maxcharacter - this.foo.length;
this.remaincharactersText = remainCharacters + “ characters remaining”;
}
}
}
}

Solution :

You should be aware that the textarea already has its maxlength set to 1000, so the label Exceeded N characters limit isn’t possible (unless you check for fewer than 1000). Currently, the label always would display N reminaing characters.

Option 1: Display count calculation inline

Instead of storing the character count (unnecessarily taking up extra memory), you could display the calculation inline with string interpolation:

<template>
<div>
<textarea v-model=”item.portfolioDescription” maxlength=”1000”></textarea>
<span>{ 1000 - item.portfolioDescription.length } remaining characters</span>
</div>
</template>

demo 1

Option 2: Display count from item variable

If you prefer storing the character count (e.g., for some internal processing), you could add a new property to the item data:

<script>
const MAXLEN = 1000

export default {
methods: {
createPortfolioItem() {
this.portfolioItems.push({
remainChars: MAXLEN, // <–
})
},
}
}
</script>

Then, update item.remainChars upon the textarea’s input-event, and display item.remainChars inline.

<template>
<div>
<textarea v-model=”item.portfolioDescription” maxlength=”1000”
@input=”item.remainChars = 1000 - item.portfolioDescription.length”>
</textarea>
<span>{ item.remainChars } remaining characters</span>
</div>
</template>

demo 2

Option 3: Display computed text

You could compute the character-count labels in a separate array that corresponds to portfolioItems:

<script>
const MAXLEN = 1000

export default {
computed: {
remainingCharsText() {
return this.portfolioItems.map(item => `${MAXLEN - item.portfolioDescription.length} remaining characters`)
},
}
}
</script>

Then, update the template to reference this computed array by index:

<template>
<div>
<textarea v-model=”item.portfolioDescription” maxlength=”1000”>
</textarea>
<span>{ remainingCharsText[index] }</span>
</div>
</template>

demo 3

Solution 2:

There are a number of ways you could do this.

In this case I think the best option is to introduce a new component to represent a single portfolio item. Each of these components can manage their own message. From their perspective there is no loop to consider.

So you’d have something like this for the list template:

<div class=”newPortfolioList”>
<my-new-portfolio-item
v-for=”(item, index) in portfolioItems”
:key=”index”
:portfolio-item=”item”
/>
</div>

Two side notes. I’ve dropped the v-bind prefix on the key, a : will suffice. I’ve also removed the this. before portfolioItems as that’s also unnecessary. The vue.js linting rules can be used to help keep this stuff in check.

There are alternatives to introducing a new component. You could generate the value of remaincharactersText within the template rather than keeping it in state. It could still be a method call but it wouldn’t be precalculated. Something like this:

<span style=”text-align:left; padding: 10px;”>{ remaincharCount(item) }</span>

A further (even more painful) alternative would be to make remaincharactersText an array of values and then grab the relevant one by index:

<span style=”text-align:left; padding: 10px;”>{ remaincharactersText[index] }</span>

But, to reiterate, introducing a separate component for the items within the v-for is probably the best way to go here.

[Vue.js] .save is not a function, and .findOneAndUpdate() doesn't update my db

I’m setting up a backend server, and to do a put method in router.
I’m using mongoose express in backend.

When i’m trying update some data in my db with .save() , i get error:

events.js:174
throw er; // Unhandled ‘error’ event
^

TypeError: PC.save is not a function

I’m trying another soulution with .findOneAndUpdate(), it is success but it doesn’t update my database.

const express = require(‘express’)
const routes = express()

const mongoose = require(‘mongoose’);
mongoose.connect(‘mongodb://localhost:27017/myapp’, { useNewUrlParser: true });
const db = mongoose.connection;
db.on(“error”, console.error.bind(console, “connection error”));
db.once(“open”, function(callback){
console.log(“Connection Succeeded”);
});

var PC = require(“../models/PC”);

//…here is my get delete etc..

This is my first solution with findOneAndUpdate

routes.put(‘/:id’, (req, res) => {
mongoose.set(‘useFindAndModify’, false);
PC.findOneAndUpdate(
{ ‘title’: req.body.title },
{ ‘$set’: {‘description’: req.body.description} },
{‘new’: true },
function(err, PC) {
if (err) {
console.log(‘ERROR WHILE PUT PC’);
throw (err);
} else {
console.log(‘Succes set’);
res.status(200).send(PC)
}
}
);
})

And this is my second solution

routes.put(‘/:id’, (req, res) => {
PC.findById(req.params.id, ‘title description’, function (error, pc) {
if (error) { console.error(error); }

PC.title = req.body.title
PC.description = req.body.description
console.log(PC);
PC.save(function (error) {
if (error) {
console.log(error)
}
res.send({
success: true,
message: ‘PC saved successfully!’,
PC: req.body
})
})
})
})

module.exports = routes;

my model:

var mongoose = require(“mongoose”);
var Schema = mongoose.Schema;

var PCSchema = new Schema({
id: Number,
brand: String,
cpu: String,
memory: Number,
type: String,
vga: String,
score: Number,
title: String,
description: String
});

var PC = mongoose.model(“PC”, PCSchema);
module.exports = PC;

Solution :

In the first example it looks like you are finding with the wrong param, and you should be using id.

Try using findByIdAndUpdate instead:

routes.put(‘/:id’, (req, res) => {
mongoose.set(‘useFindAndModify’, false);
PC.findByIdAndUpdate(
req.params.id,
{ ‘$set’: {‘description’: req.body.description, ‘title’: req.body.title} },
{‘new’: true },
function(err, pc) {
if (err) {
console.log(‘ERROR WHILE PUT PC’);
throw (err);
} else {
console.log(‘Succes set’);
res.status(200).send(pc)
}
}
);
})

In you second example, you should be calling .save on the result, not the original PC Model. You could change that to:

routes.put(‘/:id’, (req, res) => {
PC.findById(req.params.id, ‘title description’, function (error, pc) {
if (error) { console.error(error); }

// Use lowercase `pc` on all these lines
pc.title = req.body.title
pc.description = req.body.description
console.log(pc);
pc.save(function (error) {
if (error) {
console.log(error)
}
res.send({
success: true,
message: ‘PC saved successfully!’,
PC: req.body
})
})
})
})

[Vue.js] Access property in Vue Draggable move method

How can I in the method access the properties of the item belonging to the moved element? I know evt.item.id is not working…

<draggable v-model=”myarray” :move=”onMove”>
<div v-for=”item in myarray” :key=”item.id”>

methods: {
onMove(evt){
console.log(evt.item.id)
}

Solution :

item that you use is only variable name that you use in v-for. You need to access draggedContext.element for this:

methods: {
onMove(evt) {
console.log(evt.draggedContext.element.id)
}
}

[Vue.js] How to do conditional from an array object that showing value is null using v-if

there is some array like:

{
‘size’: null
}

i try using size.length / this.size.length still doesn’t work because the length is still there when size: null

<div class=”form-group” v-if=”size”>
<select class=”custom-
<option value=”” v-for=”s in size” :key=”s.id”>
{ s.size }
</option>
</select>
</div>

data(){
return{
size: null,
}
},

i would like to not show the form group when size is null using v-if

and the object is still there ( not using an empty object )

Solution :

You can do 1 of 2 things use !! Double negative operator which will convert it into a boolean, or you can simply write v-if=”size !== null

[Vue.js] Bound image source in Vue not displaying image

there is a vue.js component which is taking an object as a prop from the main App.

to access a property in that object which contains the url to the image source.

The url changes correctly. If, for example, the object’s ‘image’ property is ‘../assets/image.png’ then the src=”” will point to the correct path but nothing will show.

If I put the url in the image src manually ‘../assets/image.png’, the image displays no problem. But it to be dynamic.

<img :src=”object.image” :alt=’object.name’>

.name displays fine, but .image comes up with no image, even if the path is correct.

Solution :

You need to use require to make Webpack resolved the path correctly

<img :src=”getImage(object.image)” :alt=’object.name’>

methods: {
getImage(path) {
return require(path)
}
}

Solution 2:

It is a common issue that people use Vue. The problem is that webpack does not correctly format the binded object because expression in v-bind is executed at runtime, webpack aliases at compile time, to do that you can use webpacks require() function

methods: {
getImgUrl(imgName) {
return require(‘../assets/‘+imgName)
}
},
data () {
return {
object: {
image: ‘image.png’
}
}
}

In the template, you can get the image src as

<div>
<img :src=”getImgUrl(object.image)” :alt=”object.image”>
</div>

[Vue.js] How to write a post request to the server when the mock data is off?

llo! I need help.

there is these data - tree terms in the store.

How to write a post-request to the server to sync terms in vue.js components methods, when the mockdata is off.

P.S. ow can use Axios?

methods: {
onTermCreate(newTerm) {
const actionName = this.getModuleActionName(‘term’, ACTIONS_NAMES.CREATE_TERM);
this.$store.dispatch(actionName, newTerm).then(() => {
// TODO add post request to sync terms state with server, then reset form //
this.showPostRequestData();
this.resetForm();
})
},

addNewDataToExistingTerm(existingTerm, newTerm) {
existingTerm.parents.push(newTerm);
const actionName = this.getModuleActionName(‘term’, ACTIONS_NAMES.UPDATE_TERM);
this.$store.dispatch(actionName, existingTerm).then(() => {
// TODO add post request to sync terms state with server, then reset form //
this.showPostRequestData();
this.resetForm();
})
}
}

Solution :

Put all the API calls inside Actions in the Vuex store. This way every change that gets committed to the state only comes from inside the store and makes it easier for you to debug and make the code easier to understand.

Now to use axios you should do something like this:

state = {
//
},
getters = {
//
},
mutations = {
SET_TERM : (state,payload) => {
state.term = payload
}
},
actions = {
CREATE_TERM : async (context,payload) => {
let { data } = await Axios.get(‘/path/to/api/‘)
context.commit(‘SET_TODO’,data)
}
}

Please read this awesome article for more explanations and details.

[Vue.js] How to prevent href default behavior from changing URL name Vue js

In my component, I’ve installed a dependency called vue2-smooth-scroll that allows me to scroll to my target element based on its id. This is the code to achieve that:

<a href=”#test” class=”mouse-icon” @click.prevent=”preventDefault” v-smooth-scroll>

I was able to scroll to the element, with an id of #test as shown in the following code

<div id=”test”></div>

However, I notice that the id “test” will be appended at the end of the URL. how do you stop that from happening? For example,

http://localhost:8081/#/test

Solution :

Everything from the # on (in the URL) is known as the hash. This is used by the browser for navigation and input parameters. The hash can be changed by the browser without submitting a new request to the server.

Most likely it is how SmoothScroll is implemented, and is required. If you scroll to 2 links, you can use forward and back to navigate, or create a bookmark to a link.

Even without the plugin, browsers and URLs use the hash when navigating to internal links.

Unless you have a really good reason to remove it, I would just live with it.

Solution 2:

To prevent href default behavior from changing URL. You can listen for the click event then call preventDefault or in VueJS you can add prevent click modifier.

It would be like:

<a href=’#test’ @click.prevent>Anchor</a>

But this will not work for you case because vue2-smooth-scroll intentionally set hash on the URL. As you can see in the source code on line 48 and line 70.

So you can dirty set it back but it needs to be after animation finished.

<template>
<a href=’#test’ @click=’setUrlBack’ v-smooth-scroll>Anchor</a>
</template>

<script>
setUrlBack () {
setTimeout(() => {
history.replaceState(null, null, ‘ ‘)
}, 550) // animation duration + a little delay
}
</script>

Well, this is very bad idea.

Another option is ask the author to add support something like no-hash property.

Or just create the own plugin file then copy the code, paste and modify it.