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>
Related
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>
I am passing array as a prop to another component, and when I want to read this on mounted in that component, I got Proxy {}. How to read data from this prop? You can see in example when I want to console log prop, result is Proxy {}. I can see all values in HTML structure, but not in the console on mounted.
<template>
<div class="custom-select-container">
<div class="selected-item" #click="openSelect">
<span class="selected-items-text">{{ selectedItem.name }}</span>
<span class="icon-arrow1_b selected-items-icon" :class="{ active: showOptions }" />
</div>
<transition name="fade">
<ul v-show="options.length && showOptions" class="custom-select-options">
<li v-for="(option, index) in options" :key="index" class="custom-select-item">{{ option.name }}</li>
</ul>
</transition>
</div>
</template>
<script>
import { ref, onMounted } from 'vue'
export default {
props: {
options: {
type: Array,
default: () => []
}
},
setup(props) {
let showOptions = ref(false);
let selectedItem = ref(props.options[0])
const openSelect = () => {
showOptions.value = !showOptions.value
}
onMounted(() => {
console.log('test', props.options)
})
return {
openSelect,
showOptions,
selectedItem
}
}
}
</script>
Parent component where I am passing data:
<template>
<div class="page-container">
<div>
<div class="items-title">
<h3>List of categories</h3>
<span>({{ allCategories.length }})</span>
</div>
<div class="items-container">
<div class="item" v-for="(category, index) in allCategories" :key="index">
<span class="item-cell size-xs">{{ index + 1 }}.</span>
<span class="item-cell size-l">{{ category.name }}</span>
</div>
</div>
</div>
<custom-select
:options="allCategories"
/>
</div>
</template>
<script>
import CustomSelect from '../components/Input/CustomSelect'
import { computed } from 'vue'
import { useStore } from 'vuex'
export default {
components: {
CustomSelect
},
computed: {
},
setup() {
const store = useStore()
const allCategories = computed(() => store.getters['categories/getAllCategories'])
return {
allCategories
}
}
}
</script>
That's how reactivity works in Vue3.
use
console.log(JSON.parse(JSON.stringify(data))
or
console.log(JSON.stringify(data, null, 2))
to show the content of proxies in console
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>
I am trying to make a modal component and dismiss it when I click outside of the component. Here is my current setup:
Auth component with click event set on a div element:
<template> <div>
<transition name="modal">
<div class="modal-mask" #click="$parent.$emit('close')">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<slot name="header">Default Header</slot>
</div>
<div class="model-body">
<slot name="body">Default Body</slot>
</div>
<div class="modal-footer">
<slot name="footer">Default Footer</slot>
</div>
</div>
</div>
</div>
</transition> </div> </template>
SignIn component that injects necessary information:
<template>
<div>
<Auth />
</div>
</template>
Home component that uses the SignIn component:
<template>
<div class="home">
<SignIn v-if="showModal" #close="showModal = false" />
</div>
</template>
Right now when I click outside the modal it behaves ok, the close event is called.
But it is also called when I click inside the modal.
Not I tried to use #click.self , but now it doesn't work anymore even when clicking outside the modal.
<div class="modal-mask" #click.self="$parent.$emit('close')">
I am currently learning VueJs, but I don't understand how this works. I thought self will prevent propagating click event to child elements and thats it.
Anyone has an idea what is going on ?
PS: I am using this setup, because I want to have a SignIn and SignUp using the Auth component.
Either <div class="modal-wrapper"> or <div class="modal-container"> needs #click.prevent.stop
<template>
<div>
<transition name="modal">
<div class="modal-mask" #click="$parent.$emit('close')">
<div class="modal-wrapper">
<div class="modal-container" #click.prevent.stop>
<div class="modal-header">
<slot name="header">Default Header</slot>
</div>
<div class="model-body">
<slot name="body">Default Body</slot>
</div>
<div class="modal-footer">
<slot name="footer">Default Footer</slot>
</div>
</div>
</div>
</div>
</transition>
</div>
</template>
With this code you don't have to worry about click event's propagation #click.stop, for the style purpose I am using bootstrap.css but you can write your own style.
Here is the reusable component BsModal.vue
<template lang="pug">
div(v-if="showModal")
.modal.fade.d-block(tabindex='-1', role='dialog', :class="{'show': addShowClassToModal}")
.modal-dialog(role='document')
.modal-content.border-0
.modal-header.border-0
h5.modal-title
slot(name="title")
button.close(type='button', data-dismiss='modal', aria-label='Close', #click="hideModal")
span ×
.modal-body.p-0
slot
.modal-backdrop.fade(:class="{ 'show': addShowClassToModalBackdrop }")
</template>
<script>
export default {
name: 'BsModal',
props: {
showModal: {
default: false,
type: Boolean,
},
},
data() {
return {
addShowClassToModal: false,
addShowClassToModalBackdrop: false,
};
},
mounted() {
this.toggleBodyClass('addClass', 'modal-open');
setTimeout(() => {
this.addShowClassToModalBackdrop = true;
}, 100);
setTimeout(() => {
this.addShowClassToModal = true;
}, 400);
},
destroyed() {
this.toggleBodyClass('removeClass', 'modal-open');
},
methods: {
hideModal() {
setTimeout(() => {
this.addShowClassToModal = false;
}, 100);
setTimeout(() => {
this.addShowClassToModalBackdrop = false;
this.$emit('hide-modal', false);
}, 400);
},
toggleBodyClass(addRemoveClass, className) {
const elBody = document.body;
if (addRemoveClass === 'addClass') {
elBody.classList.add(className);
} else {
elBody.classList.remove(className);
}
},
},
};
</script>
And use it wherever you need by importing it:
<template lang="pug">
div
button(#click="showModal = true")
| Show Modal
bs-modal(
v-if="showModal",
:show-modal="showModal",
#hide-modal="showModal = false"
).modal
template(slot="title") Modal Title
// Modal Body content here
</template>
<script>
import BsModal from '~/components/BsModal.vue';
export default {
name: 'your component',
components: { BsModal },
data() {
return {
showModal: false,
};
},
};
</script>
If you don't like pug template language then you can convert PUG to HTML here: https://pughtml.com/
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>