Only show associated data in v-for - vue.js

I have the following code:
<template>
<div>
<div v-for="title in titles">
<h1>{{ title }}</h1>
<a #click="showSub">Click Here</a>
<div v-if="subshown">
Shown
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
subshown: false,
titles: []
}
},
methods: {
showSub: function () {
this.subshown = true;
// do something more
}
}
}
</script>
When i now click on the Click Here Button, the associated subshown from the current title should be shown. At the moment, when i click on Click Here, all subshown are shown.
How to implement that only the associated is shown?

Add a property called currentIndex then update it using the click event and use it in conditional rendering :
<template>
<div>
<div v-for="(title,index) in titles">
<h1>{{ title }}</h1>
<a #click="showSub(index)">Click Here</a>
<div v-if="currentIndex===index">
Shown
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
currentIndex:-1,
titles: []
}
},
methods: {
showSub: function (index) {
this.currentIndex=this.currentIndex===index?-1:index
// do something more
}
}
}
</script>

Related

can't display vuejs data property inside vue template

I'm trying to use eventbus to send data from component A:
<template>
<div v-for="(user, index) in users" :key="index" class="col-lg-6">
<div class="card card-primary card-outline">
<div class="card-body d-flex">
<h1 class="mr-auto">{{ user.name }}</h1>
Afficher
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
users: {},
}
},
methods: {
envoyerDetails($data){
Fire.$emit('envoyer_details_projet', $data);
this.$router.push('details-projet');
},
loadUser() {
if(this.$gate.isAdmin()){
axios.get("api/user").then(({ data }) => (this.users = data.data));
}
}
},
mounted() {
this.loadUser()
}
}
</script>
In component B, i receive the data and i want to display it inside the template this way:
<template>
<div class="right_col text-center" role="main">
<h5><b>name: {{ user.name }}</b> </h5>
</div>
</template>
export default {
data() {
return {
user: {},
}
},
methods: {
afficherDetails (args) {
this.user = args;
console.log(this.user.name);
}
},
mounted() {
Fire.$on('envoyer_details_projet', this.afficheDetails);
}
}
The data is not displayed in the template but it is displayed in the console. What am i missing?
Maybe when you emit the event envoyer_details_projet in component A, but component B is not mounted yet so that it can't receive the data.

Vue : params value don't change in created method

I have this link to another component with params :
<div class="dropdown-menu bg-light" aria-labelledby="navbarDropdown">
<div v-for="category in categories" :key="category.id">
<div v-if="lang=='ku'"><li><a class="dropdown-item font-weight-bold py-2" href="#" ><router-link :to="{name:'category',params:{id:category.id}}">{{category.catagory_ku}}</router-link></a></li></div>
<div v-else><li><a class="dropdown-item font-weight-bold py-2" href="#" ><router-link :to="{name:'category',params:{id:category.id}}">{{category.catagory_ar}}</router-link></a></li></div>
</div>
</div>
the category component like this :
<template>
<div style="margin-top:132px">
Categories {{id}}
</div>
</template>
<script>
export default {
data(){
return {
id :''
}
},
created(){
this.id=this.$route.params.id
}
}
</script>
when i pressed the route link the id value changed in url but in the page view it just take first id value I clicked and not be changed after I clicked another same link by different id value
Try to define the id as computed property :
<template>
<div style="margin-top:132px">
Categories {{id}}
</div>
</template>
<script>
export default {
computed:{
id(){
return this.$route.params.id
}
}
}
</script>
this should be in the mounted hook
created(){
this.id=this.$route.params.id
}
// replace with
mounted(){
this.id = this.$route.params.id
}
also if you wanna perform some extra actions related to the id changes, you can watch the route
<template>
<div style="margin-top:132px">
Categories {{ id }}
</div>
</template>
<script>
export default {
data() {
return {
id: ''
}
},
mounted() {
this.id = this.$route.params.id
},
watch: {
$route(to) {
this.id = to.params.id
// do other logics here
}
}
</script>

Is it possible to call a function in one vue component from another vue component?

LogoutModal.vue
<template>
<div class="modal LogoutModal" v-bind:class="{'is-active':confirmLogout}">
<div class="modal-background"></div>
<div class="modal-content">
</div>
<button class="modal-close is-large" aria-label="close" #click="closeModal"></button>
</div>
</template>
<script>
export default {
name: "LogoutModal",
data() {
return {
confirmLogout: false
};
},
methods: {
closeModal: function() {
this.confirmLogout = false;
},
showModal: function() {
this.confirmLogout = true;
}
}
};
</script>
Navigation.vue
<template>
<aside class="menu">
<ul class="menu-list">
<li>
<a #click="showModal">Logout</a>
</li>
</ul>
<LogoutModal />
</aside>
</template>
<script>
import LogoutModal from "#/components/LogoutModal.vue";
export default {
name: "Navigation",
components: {
LogoutModal
}
};
</script>
I want to call the showModal function when i click on the Logout link. How can i achive that?
Or is that possible to change the variable in LogoutModal.vue from Navigation.vue. I have a variable called confirmLogout in LogoutModal.vue it has to be updated from Navigation.vue. How can I do that?
You should have confirmLogout in the navigation component, then pass that into your modal, you will also need a way to close the modal so in your modal you should this.$emit('close') when your user has signaled they want to close it.
<LogoutModal :open="confirmLogout" #close="confirmLogout=false" />
In your logout modal add a prop
<script>
export default {
name: "LogoutModal",
props: ['open'],
...
then in the template
<div class="modal LogoutModal" v-bind:class="{'is-active':open}">
You could define a dynamic property in LogoutModal, that sets it's state.
Here's more about that: https://v2.vuejs.org/v2/guide/components-props.html

Vuejs How to use $emit function properly?

I have two components SingleTaskItem and ControlArea. ControlArea has a collapse button and when that button is clicked I want to call a function in SingleTaskItem. Here is my code so far. Can you please tell me what am I doing wrong?
SingleTaskItem:
<template>
<div class="SingleTaskItem">
<ControlArea v-bind:collapsed="collapsed"
v-bind:onClickCollapse="onClickCollapse"/>
</div>
</template>
<script>
export default {
name: "SingleTaskItem",
data() {
return {
collapsed: false
};
},
methods: {
onClickCollapse(value) {
console.log("on Click Collapse called");
this.collapsed = value;
}
}
};
</script>
ControlArea:
<template>
<div class="ControlArea">
<div class="action-btn edit">
<i class="fas fa-ellipsis-h"></i>
</div>
<div class="action-btn collapsible">
<i v-if="collapsed" v-on:click="uncollapse" class="fas fa-chevron-down"></i>
<i v-else v-on:click="collapse" class="fas fa-chevron-up"></i>
</div>
</div>
</template>
<script>
export default {
name: "ControlArea",
props: {
collapsed: Boolean
},
methods: {
collapse(event) {
console.log("collapse function is called");
this.$emit("onClickCollapse", "true");
},
uncollapse(event) {
this.$emit("onClickCollapse", "false");
}
}
};
</script>
Instead of v-bind:onClickCollapse="onClickCollapse" you should use v-on:onClickCollapse. This is kind of easy to miss because you used the word 'on' in your event name - it might be clearer to remove that.
Also, to pass that true/false string you need to pass $event into your function call: v-on:onClickCollapse($event). To clean this up you should probably also pass true/false booleans rather than strings.

How to Add Vue Component as an HTML Tag

I'm new to using VueJS, and I'm working on a learning project right now.
I have a component called "Draggable", and another one called "ModalPage".
"ModalPage" is as follows:
The code for this page is below:
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<button #click="propagate">Propagate</button>
<h3>Vue Modals</h3>
<ul id="list">
</ul>
<!-- <div v-html="template"></div> -->
</div>
</template>
<script>
import draggable from '#/components/Draggable.vue';
export default {
name: 'ModelPage',
components: {
draggable,
},
data () {
return {
msg: 'Welcome to the Modal Popup Page',
template: `<draggable></draggale>`,
}
},
methods: {
propagate () {
// console.log("propagated")
list.append(`<draggable></draggale>`)
}
}
}
</script>
I also have a component called "Draggable" and its code is as follows:
<template>
<div id="app">
<VueDragResize :isActive="true" :w="200" :h="200" v-on:resizing="resize" v-on:dragging="resize">
<h3>Hello World!</h3>
<p>{{ top }} х {{ left }} </p>
<p>{{ width }} х {{ height }}</p>
</VueDragResize>
</div>
</template>
<script>
import VueDragResize from 'vue-drag-resize';
export default {
name: 'app',
components: {
VueDragResize
},
data() {
return {
width: 0,
height: 0,
top: 0,
left: 0
}
},
methods: {
resize(newRect) {
this.width = newRect.width;
this.height = newRect.height;
this.top = newRect.top;
this.left = newRect.left;
}
}
}
</script>
What i want to do is to be able to click the "Propagate" button on the page, and have a <draggable></draggable> html element appended to the page, as follows:
<ul id="list">
<draggable></draggable>
</ul>
I'm completely stumped with this. Can anyone help me please?
v-if will do your job
Updated
You can use v-for as discussion:
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<button #click="propagate">Propagate</button>
<h3>Vue Modals</h3>
<ul id="list">
<draggable v-for="index in count" :key="index>
</draggable>
</ul>
<!-- <div v-html="template"></div> -->
</div>
</template>
<script>
import draggable from '#/components/Draggable.vue';
export default {
name: 'ModelPage',
components: {
draggable,
},
data () {
return {
msg: 'Welcome to the Modal Popup Page',
template: `<draggable></draggale>`,
count: 0
}
},
methods: {
propagate () {
// console.log("propagated")
this.count++
}
}
}
</script>