Show child of this parent click vue - vue.js

I want to show the immediate child after clicking on its parent. Its easy on jquery, hard on vue, how can I do it?
template:
<boxes inline-template>
<div class="white-box" #click="toggleTick">Purchase only
<div v-if="showTick"><i class="fas fa-check"></i></div>
</div>
</boxes>
js
Vue.component('boxes', {
data: function () {
return {
showTick: false
}
},
methods: {
toggleTick () {
this.showTick = !this.showTick
}
}
})
var app = new Vue({
el: '#app',
data: {
}
})
At the moment I have multiple "white-box" div, it shows the child div for all of them, I just want to show the div for the clicked parent's child.

You should have behavior that you expect :
Vue.component('boxes', {
data: function () {
return {
showTick: false
}
}
})
var app = new Vue({
el: '#app'
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<boxes inline-template>
<div class="white-box" #click="showTick = !showTick">
<span>
Purchase only
</span>
<div v-if="showTick">Component 1</div>
</div>
</boxes>
<boxes inline-template>
<div class="white-box" #click="showTick = !showTick">
<span>
Purchase only
</span>
<div v-if="showTick">Component 2</div>
</div>
</boxes>
</div>

Your white-boxes are sharing the same showTick variable, so clicking on one fo them changes the value for all of them.
There are 2 available solutions to this:
Have multiple boxes components and not multiple white-boxes under the same boxes component. Something take ends up looking like this in the DOM
<boxes>...</boxes>
<boxes>...</boxes>
<boxes>...</boxes>
Use an array for showTick and use an index when calling toggleClick. Also note that for the changes in the array to be reactive you need to use Vue.set.
I recommend the former solution.

Related

Vue component not showing no errors

I am trying to do a very simple vue example and it won't display. I've done similar things before, but this won't work.
It is an extremely simple task list. It is an input with a submit button that adds an item to a list. For some reason the component does not render at all. I am very lost am supposed to give a presentation on vue. I was hoping to use this as an example.
I'm really not sure what else to say about this, but stack overflow won't let me submit this without typing more information about the issue.
<div id="app">
<task-list></task-list>
</div>
Vue.component('task-list-item', {
props: ["task"],
template: '#task-list-item-template'
})
Vue.component('task-list', {
data: function () {
return {
taskList: [],
newTask: ''
}
},
methods: {
addTask: function () {
var self = this;
if (self.newTask !== ""
&& self.newTask !== null
&& typeof self.newTask !== "undefined") {
this.taskList.push(self.newTask);
this.newTask = "";
}
}
},
template: '#task-list-template'
})
new Vue({
el: '#app',
data: function () {
return {
}
}
})
<script id="task-list-template" type="text/x-template">
<input v-model="newTask" />
<button v-on:click="addTask()">Add Task</button>
<ul>
<task-list-item v-for="taskItem in taskList"
v-bind:task="taskItem">
</task-list-item>
</ul>
</script>
<script id="task-list-item-template" type="text/x-template">
<li>{{task}}</li>
</script>
I am getting no error messages of any kind.
I think the problem is there should be only 1 child under <script id="task-list-template" type="text/x-template"></script>. In task-list-template, you have multiple children. Try to wrap them in 1 div
<script id="task-list-template" type="text/x-template">
<div>
<input v-model="newTask" />
<button v-on:click="addTask()">Add Task</button>
<ul>
<task-list-item v-for="taskItem in taskList"
v-bind:task="taskItem">
</task-list-item>
</ul>
</div>
</script>
Demo on codepen
According to A Single Root Element
Every component must have a single root element
To fix you can do some thing like:
<script id="task-list-template" type="text/x-template">
<div>
<input v-model="newTask" />
<button v-on:click="addTask()">Add Task</button>
<ul>
<task-list-item v-for="taskItem in taskList" v-bind:task="taskItem">
</task-list-item>
</ul>
</div>
</script>

How to append a component on button click

I have a component that I have loading in app.vue. I have a button that I'm using to call a method to try and add another instance of that component so I can have however many instances of that component on the page
When I call div.append(EquipmentInput);, it just appends [object Object] to the DOM.
html:
<div id="eDiv">
<EquipmentInput></EquipmentInput>
<button class="block mx-auto px-4 py-2 rounded-full bg-gray-200 hover:bg-blue-300 hover:font-bold" v-on:click.prevent="addEquipmentLine">+</button>
</div>
<div class="text
method:
addEquipmentLine(){
let eDiv = document.getElementById('eDiv');
eDiv.append(EquipmentInput);
},
I think it should be appending another instance of the component, but it's just appending that object text.
I believe you do something like this: Inserting in DOM.
Use a $ref instead of an ID
Create a new element and $mount it
Get the $ref of the parent and append the DOM element that way
const EquipmentInput = Vue.extend({
template: "<div>Hello World</div>"
});
const app = new Vue({
el: "#app",
components: {
EquipmentInput
},
methods: {
add() {
const instance = new EquipmentInput();
instance.$mount();
this.$refs.ediv.appendChild(instance.$el);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div ref="ediv"></div>
<button #click="add">Click Me</button>
</div>

v-on:click problem if the elements has another element inside

I am dealing with the problem of my click event in vue js. I made a click even on element that has another element inside it.
Here's my code:
<span class="pull-down-controller" #click="pullDown($event)">
<span class="indicator">-</span> Controller
</span>
in the frontend it will show - Controller
if I click the word Controller it will call the specified function which is pullDown() but why is it whenever I click the indicator or the minus symbol, it will not do anything even if it is inside the <span> where I put the #click event?
The reason why I put a <span> inside it so I can change the symbol to + using jquery.
thanks!
No need for jQuery, Vue's reactivity provides all you need:
new Vue({
el: '#app',
template: `
<span #click="pullDown" style="font-size: 48px;">
<span>{{ indicator }}</span> Controller
</span>
`,
data () {
return {
expanded: false
}
},
computed: {
indicator () {
return this.expanded ? '+' : '-'
}
},
methods: {
pullDown (event) {
this.expanded = !this.expanded
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>

Vue.JS Passing object to child for use inside slot

I am trying to pass an object that is loaded within DataContainer into a slot, so that the user can customise the view.
<data-container silo-id="5">
<div slot="content"> <!-- I tried :data="siloData" here but no luck -->
Your current balance is {{data.balance}}
</div>
</data-container>
So DataContainer loads the resource via http and sets the value to its 'siloData' property.
DataContainer's template has no content of its own just a placeholder for the slot.
<template>
<div>
<slot name="content"></slot>
</div>
</template>
When I try this the text is not interpolated and just remains as {{siloData.balance}} to the browser.
I have tried some examples from Vue.JS site like the todo list, but I must admit utterly confused, maybe because this is not a collection, but just a single (albeit complex) object.
Hopefully someone can point me in the right direction.
Many thanks
Phil
You can use a $emit event
Vue.component('data-container', {
template: '#data-container',
data() {
return {
siloData: {}
}
},
mounted() {
this.siloData = { name: "Silo", balance: 10 } // loading data
this.$emit('silo-loaded', this.siloData)
}
})
new Vue({
el: '#app',
data() {
return {
data: {}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app">
<data-container class="card" #silo-loaded="val => data = val">
<div slot="content">
Your current balance is {{ data.balance }}
</div>
</data-container>
</div>
<template id="data-container">
<div>
<slot name="content"></slot>
</div>
</template>

Vue custom filtering input component

I'am trying to create a component that have 'just' an text input. String typed in this input will be used to filter a list. My problem is that I cannot handle how to share this filter string between my component and the main app that contains the list to filter.
I tried several things and most of the time I get the error :
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value
So I looked Vuex but I thinks it cannot help in this case because I can have several filter component used in he same page for different list, and I don't want them to be synchronized ^^
Here is what I have:
The filter component
<script type="x/template" id="filterTpl">
<div>
<span class="filter-wrapper">
<input type="search" class="input input-filter" v-model.trim="filter" />
</span>
</div>
</script>
<script>
Vue.component('list-filter', {
props: {
filter: String
}
template: '#filterTpl'
});
</script>
And my main app:
<div id="contacts">
<list-filter :filter="filter"></list-filter>
<ul class="contacts-list managed-list flex">
<li class="contact" v-for="contactGroup in filteredData">
[...]
</li>
</ul>
</div>
<script>
var contactsV = new Vue({
el: '#contacts',
data: {
filter: "",
studyContactsGroups: []
},
computed: {
filteredData: function(){
// Using this.filter to filter the studyContactsGroups data
[...]
return filteredContacts;
}
}
});
</script>
Thanks for any help or tips :)
You can synchronize child value and parent prop either via explicit prop-event connection or more concise v-bind with sync modifier:
new Vue({
el: '#app',
data: {
rawData: ['John', 'Jane', 'Jim', 'Eddy', 'Maggy', 'Trump', 'Che'],
filter: ''
},
components: {
'my-input' : {
// bind prop 'query' to value and
// #input update parent prop 'filter' via event used with '.sync'
template: `<input :value="query" #input="updateFilter">`,
props: ['query'],
methods: {
updateFilter: function(e) {
this.$emit('update:query', e.target.value) // this is described in documentation
}
}
}
},
computed: {
filteredData: function() {
// simple filter function
return this.rawData.filter(el => el.toLowerCase()
.match(this.filter.toLowerCase()))
}
}
});
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<my-input :query.sync="filter"></my-input>
<hr>
<ul>
<li v-for="line in filteredData">{{ line }}</li>
</ul>
</div>