dynamically add a property to an object in an array - vue.js

I am trying to add a property in an object inside an array, and I just cant make it work:
So I am passing an array to Names.vue which contains ['John', 'Jane'], and I am trying to show some icons next to the name when the user hover over that name:
A live example of the problem
Names.vue
<template>
<div>
<ul>
<li v-for="(name, index) in names" :key="index">
<span #mouseenter="name.showIcons = true" #mouseleave="name.showIcons = false">{{ name }}</span>
<span v-if="name.showIcons" style="margin-left:10px">icons</span>
</li>
</ul>
</div>
</template>
<script>
export default {
props: {
names: Array
}
};
</script>
But this doesn't work? Why?

Alternatively - you can use this.$set:
<template>
<div id="app">
<Names :names="names"/>
</div>
</template>
<script>
import Names from "./components/Names";
export default {
name: "App",
components: {
Names
},
data() {
return {
names: [{ name: "John" }, { name: "Jane" }]
};
}
};
</script>
<template>
<div>
<ul>
<li v-for="(name, index) in names" :key="index">
<span
#mouseenter="setIconsState(index, true)"
#mouseleave="setIconsState(index, false)"
>{{ name.name }}</span>
<span v-if="name.showIcons" style="margin-left:10px">icons</span>
</li>
</ul>
</div>
</template>
<script>
export default {
props: {
names: Array
},
methods: {
setIconsState(index, isShow) {
this.$set(this.names[index], `showIcons`, isShow);
}
}
};
</script>

Related

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>

how do i render searched beer/beers?

hello I want to know how ik can render searched beers in vue
I have a component that renders a list of beers. I also have an component that searches beers. what i want to do is that when I search, my beer list component updates the list and shows me only the list of beers i searched.
this is my beer list component
<template>
<div class="container">
<div>
<Search v-bind:allBeers="allBeers" />
</div>
<div>
<b-card
v-bind:key="beer.id"
v-for="beer in allBeers "
:img-src="beer.image_url"
:alt="beer.name"
img-top
tag="article"
style="max-width: 22rem ;"
class="mb-2"
>
<b-card-text class="c-text">
<h4 class="title">{{ beer.name }}</h4>
<p>{{ beer.ingredients.malt[0].name }}</p>
<router-link
:to="{
name: 'BeerDetails',
params: { id: beer.id, beer:beer },
}"
>
<b-button class="link" size="sm" variant="outline-warning">View Beer details</b-button>
</router-link>
</b-card-text>
</b-card>
</div>
</div>
</template>
<script>
import Search from "./Search";
import { mapGetters, mapActions } from "vuex";
export default {
components: { Search },
name: "Beers",
// props: ["beers"],
methods: { ...mapActions(["fetchBeers"]) },
computed: mapGetters(["allBeers"]),
created() {
this.fetchBeers();
}
};
</script>
and this is my search component here I filter beers that match my input
<template>
<div>
<b-form #submit="onSubmit" #reset="onReset">
<b-form-group id="input-group-1">
<b-form-input id="input-1" v-model="form.search" type="text" required placeholder="search"></b-form-input>
</b-form-group>
</b-form>
</div>
</template>>
<script>
export default {
name: "Search",
props: ["allBeers"],
data() {
return {
form: {
search: ""
}
};
},
methods: {
onSubmit(evt) {
evt.preventDefault();
this.allBeers.filter(beer => {
console.log(beer.name.toLowerCase() == this.form.search);
});
//alert(JSON.stringify(this.form));
},
onReset(evt) {
evt.preventDefault();
this.form.search = "";
}
}
};
</script>
<style scoped>
</style>
How would I use the search component in my beer component to display only the searched beers?
In the search component set a property to each beer, something like this:
onSubmit(evt) {
evt.preventDefault();
this.allBeers.forEach(beer => {
beer.hidden = (beer.name.toLowerCase() == this.form.search);
});
},
And in the beers component, hide the irrelevant beers:
<template>
<div class="container">
<div>
<Search v-bind:allBeers="allBeers" />
</div>
<div>
<div v-for="beer in allBeers" v-bind:key="beer.id">
<b-card v-if="!beer.hidden"
...
</b-card>
</div>
</div>
</div>
</template>

Conditional rendering template on Vue?

I have a simple situation that I want to render cart-badge based on window width value but it always show the last one even though windowWidth does change the value. Please advise me what to do !
<template v-if="windowWidth>=1024">
<div class="nav-user-account"> {{windowWidth}}
<div class="nav-cart nav-cart-box">
<span class="text hidden-sm">Cart</span>
<span class="cart-number" id="nav-cart-num">{{cartItemCount}}</span>
</div>
</div>
</template>
<template v-if="windowWidth<1024">
<a href="#" style="color:#ff6a00" class="icon-cart">
{{windowWidth}}
<i class="fa fa-shopping-cart fa-2x" aria-hidden="true"></i>
<span class="cart-num">2</span>
</a>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
data() {
return {
windowWidth: window.innerWidth,
}
},
computed: {
...mapGetters('cart', {
cartItemCount: 'getShoppingCartItemsCount'
})
},
mounted() {
this.$nextTick(() => {
window.addEventListener('resize', () => {
this.windowWidth= window.innerWidth
});
})
},
}
</script>
UPDATED: as Tony19 pointed out, i need a master template outside of these 2 template.
<template>
<div>
<template v-if="windowWidth>=1024">...</template>
<template v-else></template>
</div>
</template>

Send data from one component to another in vue

Hi I'm trying to send data from one component to another but not sure how to approach it.
I've got one component that loops through an array of items and displays them. Then I have another component that contains a form/input and this should submit the data to the array in the other component.
I'm not sure on what I should be doing to send the date to the other component any help would be great.
Component to loop through items
<template>
<div class="container-flex">
<div class="entries">
<div class="entries__header">
<div class="entries__header__title">
<p>Name</p>
</div>
</div>
<div class="entries__content">
<ul class="entries__content__list">
<li v-for="entry in entries">
{{ entry.name }}
</li>
</ul>
</div>
<add-entry />
</div>
</div>
</template>
<script>
import addEntry from '#/components/add-entry.vue'
export default {
name: 'entry-list',
components: {
addEntry
},
data: function() {
return {
entries: [
{
name: 'Paul'
},
{
name: 'Barry'
},
{
name: 'Craig'
},
{
name: 'Zoe'
}
]
}
}
}
</script>
Component for adding / sending data
<template>
<div
class="entry-add"
v-bind:class="{ 'entry-add--open': addEntryIsOpen }">
<input
type="text"
name="addEntry"
#keyup.enter="addEntries"
v-model="newEntries">
</input>
<button #click="addEntries">Add Entries</button>
<div
class="entry-add__btn"
v-on:click="openAddEntry">
<span>+</span>
</div>
</div>
</template>
<script>
export default {
name: 'add-entry',
data: function() {
return {
addEntryIsOpen: false,
newEntries: ''
}
},
methods: {
addEntries: function() {
this.entries.push(this.newEntries);
this.newEntries = '';
},
openAddEntry() {
this.addEntryIsOpen = !this.addEntryIsOpen;
}
}
}
</script>
Sync the property between the 2:
<add-entry :entries.sync="entries"/>
Add it as a prop to the add-entry component:
props: ['entries']
Then do a shallow merge of the 2 and emit it back to the parent:
this.$emit('entries:update', [].concat(this.entries, this.newEntries))
(This was a comment but became to big :D)
Is there a way to pass in the key of name? The entry gets added but doesn't display because im looping and outputting {{ entry.name }}
That's happening probably because when you pass "complex objects" through parameters, the embed objects/collections are being seen as observable objects, even if you sync the properties, when the component is mounted, only loads first level data, in your case, the objects inside the array, this is performance friendly but sometimes a bit annoying, you have two options, the first one is to declare a computed property which returns the property passed from the parent controller, or secondly (dirty and ugly but works) is to JSON.stringify the collection passed and then JSON.parse to convert it back to an object without the observable properties.
Hope this helps you in any way.
Cheers.
So with help from #Ohgodwhy I managed to get it working. I'm not sure if it's the right way but it does seem to work without errors. Please add a better solution if there is one and I'll mark that as the answer.
I follow what Ohmygod said but the this.$emit('entries:update', [].concat(this.entries, this.newEntries)) didn't work. Well I never even need to add it.
This is my add-entry.vue component
<template>
<div
class="add-entry"
v-bind:class="{ 'add-entry--open': addEntryIsOpen }">
<input
class="add-entry__input"
type="text"
name="addEntry"
placeholder="Add Entry"
#keyup.enter="addEntries"
v-model="newEntries"
/>
<button
class="add-entry__btn"
#click="addEntries">Add</button>
</div>
</template>
<script>
export default {
name: 'add-entry',
props: ['entries'],
data: function() {
return {
addEntryIsOpen: false,
newEntries: ''
}
},
methods: {
addEntries: function() {
this.entries.push({name:this.newEntries});
this.newEntries = '';
}
}
}
</script>
And my list-entries.vue component
<template>
<div class="container-flex">
<div class="wrapper">
<div class="entries">
<div class="entries__header">
<div class="entries__header__title">
<p>Competition Entries</p>
</div>
<div class="entries__header__search">
<input
type="text"
name="Search"
class="input input--search"
placeholder="Search..."
v-model="search">
</div>
</div>
<div class="entries__content">
<ul class="entries__content__list">
<li v-for="entry in filteredEntries">
{{ entry.name }}
</li>
</ul>
</div>
<add-entry :entries.sync="entries"/>
</div>
</div>
</div>
</template>
<script>
import addEntry from '#/components/add-entry.vue'
import pickWinner from '#/components/pick-winner.vue'
export default {
name: 'entry-list',
components: {
addEntry,
pickWinner
},
data: function() {
return {
search: '',
entries: [
{
name: 'Geoff'
},
{
name: 'Stu'
},
{
name: 'Craig'
},
{
name: 'Mark'
},
{
name: 'Zoe'
}
]
}
},
computed: {
filteredEntries() {
if(this.search === '') return this.entries
return this.entries.filter(entry => {
return entry.name.toLowerCase().includes(this.search.toLowerCase())
})
}
}
}
</script>

Vue.js - component that binds a binary string to checkbox inputs

In Vue.js, how would I write a component that binds to a string of 1s and 0s, such as "111001011", and represents this data to the user as a set of named checkboxes?
Maybe this is what you'r looking for:
App.vue
<template>
<div id="app">
<checkboxes :binary="binary"></checkboxes>
</div>
</template>
<script>
import Checkboxes from './components/Checkboxes'
export default {
name: 'app',
data(){
return {
binary: "11001011"
};
},
components: {
Checkboxes
}
}
</script>
Checkboxes.vue:
<template>
<div>
<ul>
<li v-for="position in binary.length">
<label>
<input type="checkbox" :name="binary[position - 1]" :checked="binary[position - 1] == '1' ? true : false"> {{ binary[position - 1] }}
</label>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'checkboxes',
props: {
binary: {
required: true,
type: String
}
}
}
</script>
This will go through the string length and for each character will mark as checked/unchecked based on the binary value(1/0)
Result: