Refactoring Vue.js v2 to Vue.js 3 - vue.js

How can I refactor my Vue2 components to Vue3? It's not clear from the docs.
<template>
<div>
<main class="flex-1">
<h1>{{ main_text }}</h1>
<router-view :key="$route.fullPath"></router-view>
</main>
</div>
</template>
<script>
export default {
mounted() {
if (!this.user_id) {
return this.$router.push("/login");
}
},
created() {
this.setMainText('Welcome')
},
props: ["user_id"],
data() {
return {
main_text: 'Hello World',
};
},
methods: {
setMainText(text) {
this.main_text = text;
},
},
};
</script>

Related

How can I use the method from other component

I want use the method 'pause()' in the component 'vue-count-to',but the webstorm tips Unresolved function or method pause() .How can I use the method in 'vue-count-to'?Thank you
<template>
<div>
<countTo ref="countTo1" :startVal='startVal' :endVal='endVal' :duration='3000'>
</countTo>
<input type="text" v-model="endVal">
<Button v-on:click="handleClick" >reset</Button>
</div>
</template>
<script>
import CountTox from 'vue-count-to';
export default {
components: {
countTo: CountTox},
data () {
return {
startVal: 0,
endVal: 2017,
autoplay: false
}
},
methods: {
handleClick() {
this.$refs.countTo1.pause();
}
}
}
</script>
you can use $root.$emit() and $root.$on()
const componentA = {
template: `
<button #click="callMethodInComponentB">
call method in component-b
</button>
`,
methods: {
callMethodInComponentB() {
this.$root.$emit('call-to-component-b', 2);
}
}
}
const componentB = {
template: `
<h1>Value: {{ value.toString() }}</h1>
`,
data(){
return {
value: 0
}
},
methods: {
plus(add) {
this.value += add
}
},
mounted() {
this.$root.$on('call-to-component-b', add => {
this.plus(add)
});
}
};
new Vue({
el: '#app',
components: {
componentA,
componentB
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<main id="app">
<component-a></component-a>
<component-b></component-b>
</main>
define the method in App.vue and access it from probs and emit

VueJS child component button changes class of parent

I have a Navigation component with a button to toggle dark mode/light mode. When I click this button, I would like to change a class in the App.vue. How to pass the data from the button to the App.vue?
I believe I have to emit something from the Top Nav and v-bind the class in App.vue, but I don't quite understand how to get it change the class.
When I run this, the button does not change the class on the div.
App.vue
<template>
<div id="app" :class="[isActive ? 'darkmode' : '']>
<header>
<top-nav #change-mode="enableDarkMode"></top-nav>
</header>
...
</div>
</template>
<script>
export default {
name: "App",
components: {
TopNav,
},
props: ['isActive'],
data() {},
methods: {
enableDarkMode(isActive) {
console.log(isActive)
},
},
};
</script>
Top Nav Component
<template>
...
<div>
<button
:class="[isActive ? 'dark' : 'light']"
#click="toggle">
{{ isActive ? "DarkMode" : "LightMode" }}
</button>
</div>
</template>
<script>
export default {
name: "TopNav",
data() {
return {
isActive: false,
};
},
components: {},
methods: {
toggle() {
this.isActive = !this.isActive;
this.$emit('change-mode', this.isActive )
console.log('emit child')
},
},
};
</script>
From the snippet you provided it seems like the App.vue has isActive props instead of data for the method enableDarkMode to manage. Vue's props is not to be updated by the component they belong to because of how the data flow works in Vue props. With the App.vue being the parent of Top Nav component, you probably want it to be like this:
App.vue
<template>
<div id="app" :class="[isActive ? 'darkmode' : '']>
<header>
<top-nav #change-mode="enableDarkMode"></top-nav>
</header>
...
</div>
</template>
<script>
export default {
name: "App",
components: {
TopNav,
},
// this is probably not needed because App.vue is the parent component
// props: ['isActive'],
data() {
return {
isActive: false,
};
},
methods: {
enableDarkMode(isActive) {
// manages the data
this.isActive = isActive;
},
},
};
</script>
Top Nav component
<template>
...
<div>
<button
:class="[isActive ? 'dark' : 'light']"
#click="toggle">
{{ isActive ? "DarkMode" : "LightMode" }}
</button>
</div>
</template>
<script>
export default {
name: "TopNav",
data() {
return {
isActive: false,
};
},
components: {},
methods: {
toggle() {
this.isActive = !this.isActive;
this.$emit('change-mode', this.isActive )
console.log('emit child')
},
},
};
</script>

Watching properties in child component does not work

I have 2 components, a parent and a child. I want to modify the value of a prop in my parent component (by calling a method when a button is clicked) and send it to the child component. In the child component, I want to watch for changes in my prop, so that anytime it changes, it does something (for testing purposes, I tried to console.log() the prop).
Parent:
<template>
<div>
<h5>Your Feeds</h5>
<div id="feeds">
<div class="card" v-for="feed in feeds">
<div class="card-body" :id="feed['_id']" >
{{ feed['name'] }}
<button v-on:click="loadFeed(feed['_id'])">Button</button>
</div>
</div>
</div>
</div>
</template>
<script>
import GridComponent from "./GridComponent";
export default {
name: "FeedsListComponent",
data() {
return {
feeds: []
}
},
mounted() {
axios
.get("/getFeeds")
.then(response => (this.feeds = response.data))
.catch(error => console.log(error))
},
methods: {
loadFeed(id) {
this.feedId = id
}
},
components: {
GridComponent
}
}
</script>
Child:
<template>
<div id="grid">
<v-grid
theme="compact"
:source="rows"
:columns="columns"
></v-grid>
</div>
</template>
<script>
import VGrid from "#revolist/vue-datagrid";
export default {
name: "Grid",
props: ['feedId'],
data() {
return {
columns: [],
rows: [],
};
},
watch: {
feedId: function(val, oldVal) {
console.log(val)
console.log(oldVal)
console.log(this.feedId)
//here I want to send an ajax request with feedId to one of my controllers in order to get
//the data needed for rows and colums
}
},
components: {
VGrid,
},
};
</script>
I put together a sample that is working in order to help you diagnose why yours isn't working:
Parent.vue
<template>
<div class="parent">
<h3>Parent</h3>
<div class="row">
<div class="col-md-6">
<button class="btn btn-secondary" #click="incrementCounter">Change parent message</button>
</div>
</div>
<child :propMessage="message" />
</div>
</template>
<script>
import Child from '#/components/stackoverflow/watch-prop/Child'
export default {
components: {
Child
},
data() {
return {
counter: 0
}
},
computed: {
message() {
return 'Message' + this.counter;
}
},
methods: {
incrementCounter() {
this.counter++;
}
}
}
</script>
Child.vue
<template>
<div class="child">
<hr>
<div class="row">
<div class="col-md-6">
<label>Message in child from watched prop:</label>{{ dataMessage }}
</div>
</div>
</div>
</template>
<script>
export default {
props: {
propMessage: {
type: String,
required: true
}
},
data() {
return {
dataMessage: this.propMessage
}
},
watch: {
propMessage(newMessage) {
this.dataMessage = newMessage;
}
}
}
</script>
<style scoped>
label {
font-weight: bold;
margin-right: 0.5rem;
}
</style>

Vue resets child Component data on props update

I have child component, that has some internal data, that should not be changed from outside. But when I update prop from parent component, this internal data is reset.
Basically in example below, when we change title from outside, value is set back to empty ''. How can I make value persistent with Child component props update?
Child.vue
<template>
<div class="child">
<h2>{{title}}</h2>
<input type="text" :value="value" v-on:change="$emit('change', $event.target.value)">
</div>
</template>
<script>
export default {
props: {
title: {
default: 'Just title'
}
},
data() {
return {
value: ''
}
}
}
</script>
Parent.vue
<template>
<div class="parent">
<Child :title="title" v-on:change="processTitle($event)"></Child>
</div>
</template>
<script>
import Child from './Child';
export default {
data() {
return {
title: 'Title from parent'
}
},
methods: {
processTitle(value) {
this.title = value.reverse();
}
}
}
</script>
You are not setting the value data attribute, :value=value means that "if value changes, the input value should pick up that change". But value doesn't change. Use v-model instead if you want to keep it simple.
Vue.component("Child", {
props: {
title: {
default: 'Just title'
}
},
data() {
return {
value: ''
}
},
template: `
<div class="child">
<h2>{{title}}</h2>
<input type="text" v-model="value" v-on:change="$emit('change', $event.target.value)">
</div>
`
})
new Vue({
el: "#app",
data() {
return {
title: 'Title from parent'
}
},
methods: {
processTitle(value) {
this.title = value.split("").reverse().join("");
}
},
template: `
<div class="parent">
<child :title="title" v-on:change="processTitle($event)"></child>
</div>
`
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>
EDIT
Also, if you want a continuous effect, don't use #change - use #input instead:
Vue.component("Child", {
props: {
title: {
default: 'Just title'
}
},
data() {
return {
value: ''
}
},
template: `
<div class="child">
<h2>{{title}}</h2>
<input type="text" v-model="value" v-on:input="$emit('change', $event.target.value)">
</div>
`
})
new Vue({
el: "#app",
data() {
return {
title: 'Title from parent'
}
},
methods: {
processTitle(value) {
this.title = value.split("").reverse().join("");
}
},
template: `
<div class="parent">
<child :title="title" v-on:change="processTitle($event)"></child>
</div>
`
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>

how to hide sidebar in vue

guys, I m new to Vue and taken a coreui admin panel to develop some font vue but now I got stuck in this problem this is nav.js file
export default {
items: [
{
name: 'Product',
url: '/product',
icon: 'fa fa-cart-arrow-down',
children: [
{
name: 'Addproduct',
url: '/product/Addproduct',
},
{
name: 'Listproduct',
url: '/product/Listproduct',
}
]
},
]
}
main container
<template>
<div class="app">
<div class="app-body">
<Sidebar :navItems="nav"/>
<main class="main">
<div class="container-fluid">
<router-view></router-view>
</div>
</main>
<AppAside/>
</div>
</div>
</template>
<script>
import nav from '../_nav'
export default {
name: 'full',
components: {
Sidebar,
},
data () {
return {
nav: nav.items
}
},
computed: {
name () {
return this.$route.name
},
list () {
return this.$route.matched
}
}
}
</script>
here is my sidebar
<template v-for="(item, index) in navItems">
<template v-if="item.title">
<SidebarNavTitle :name="item.name" :classes="item.class" :wrapper="item.wrapper"/>
</template>
<template v-else>
<template v-if="item.children">
</template>
<template v-else>
<SidebarNavItem :classes="item.class">
<SidebarNavLink :name="item.name" :url="item.url" :icon="item.icon" :badge="item.badge" :variant="item.variant"/>
</SidebarNavItem>
</template>
</template>
</template>
i m stroing addproduct in my browser local storage now if when user login and go to dashboard then my i watch which url name is present in browser application or not if present show that else ignore now my problem is that how i can apply if condition like addproduct=addprodcut this this visible else hide
You could have a method in mounted hook, which can fetch data from localstorage and check if it's present in the url or not. Then assign it to a variable in main component which toggles the sidebar. Something like below should work:
<template>
<div class="app">
<div class="app-body">
<Sidebar :navItems="nav" v-if="showSidebar" />
<main class="main">
<div class="container-fluid">
<router-view></router-view>
</div>
</main>
<AppAside/>
</div>
</div>
</template>
<script>
import nav from '../_nav'
export default {
name: 'full',
components: {
Sidebar,
},
data () {
return {
nav: nav.items,
showSidebar: false
}
},
mounted () {
this.checkSidebarVisibility()
},
methods: {
checkSidebarVisibility: function() {
const inLocal = window.localStorage.getItems('your_item');
const inUrl = window.location.toString();
// check if inurl inside inLocal
if (inUrl is in inLocal) {
this.showSidebar = true;
} else {
this.showSidebar = false;
}
}
},
computed: {
name () {
return this.$route.name
},
list () {
return this.$route.matched
}
}
}
</script>