Vue.js: How to change a class when a user has clicked on a link? - css

I am trying to add a class to an element depending on whether the user has clicked on a link. There is a similar question here but it is not working as I wanted it to be.
I created a component which has its own internal data object which has the property, isShownNavigation: false. So when a user clicks on the a I change isShownNavigation: true and expect my css class isClicked to be added. Alas that is not happening - isShownNavigation stays false in the component when I displayed it {{isShownNavigation}} but I can see in the console that my method is working when clicked.
I imported my header component to the App. Code is below.
Header Component
<template>
<header class="header">
<a
href="#"
v-bind:class="{isClicked: isShowNavigation}"
v-on:click="showNavigation">
Click
</a>
</header>
</template>
<script>
export default {
name: 'header-component',
methods: {
showNavigation: () => {
this.isShowNavigation = !this.isShowNavigation
}
},
data: () => {
return {
isShowNavigation: false
}
}
}
</script>
Application
<template>
<div id="app">
<header-component></header-component>
</div>
</template>
<script>
import HeaderComponent from './components/Header.vue'
export default {
name: 'app',
components: {
'header-component': HeaderComponent
}
}
</script>
I am using the pwa template from https://github.com/vuejs-templates/pwa.
Thanks.

Don't use fat arrow functions to define your methods, data, computed, etc. When you do, this will not be bound to the Vue. Try
export default {
name: 'header-component',
methods: {
showNavigation(){
this.isShowNavigation = !this.isShowNavigation
}
},
data(){
return {
isShowNavigation: false
}
}
}
See VueJS: why is “this” undefined? In this case, you could also really just get rid of the showNavigation method and set that value directly in your template if you wanted to.
<a
href="#"
v-bind:class="{isClicked: isShowNavigation}"
v-on:click="isShowNavigation = true">
Click
</a>
Finally, if/when you end up with more than one link in your header, you will want to have a clicked property associated with each link, or an active link property instead of one global clicked property.

Related

Vue draggable change parent data when using KeepAlive

I have a vue3 app, and one of the child component uses vue-draggable.
In the parent component I have an object (let's call it myJson) which propagates to child component with props.
So far it works as expected.
However, when adding 'KeepAlive' to the parent component, every time I drag the items, myJson is set to the drag event instead of the origin data it had.
It still occures even if I pass to the child component a copy of myJson (with JSON parse-JSON stringify). See details below
parent component:
<template>
<KeepAlive>
<component :is="activeComponent" :my-json="myJson" />
</KeepAlive >
</template>
data: () => ({
myJson: { ...someData }
})
mid component:
<template>
<list-items :items="items" />
</template>
<script>
export default {
components: { ListItems },
computed: {
items() {
return JSON.parse(JSON.stringify(this.myJson.value.items))
}
},
}
</script>
child component (ListItems):
<template>
<draggable
v-model="items"
animation="100"
handle=".dnd-handle"
item-key="product"
class="items-list"
#start="drag=true"
#end="drag=false"
>
<template #item="{ element, index }">
{{element}}
</template>
</draggable>
</template>
<script>
import draggable from 'vuedraggable'
export default {
components: { draggable },
props: ['items'],
}
</script>
The items are displayed correctly in the component.
Before dragging, myJson is an object with my data.
After dragging myJson is an event.
Any idea?
vuedraggable version is 4.1.0
--UPDATE--
In parent component there is a function "update", which gets value and updates myJson.
methods: {
update (value) {
myJson = value
}
}
I found out that every time I drag, there is a call to this function with the dragging event as value, even when I try to catch the draggable events. Thats why myJson gets wrong value.
My problem was solved when I changed the function's name. But anyone knows why this happens?

Problem creating a WebComponent from a SFC with Vue 3.2.9

I am trying to create a web component in Vue3. For this I use the Vue cli with the target library. Everything works as expected.
The problem is that I can't get any values from the props parameter of the setup function.
If I use the component as a Vue component the props work. As a web component i cant get any value from the props parameter
Its seams that the property wc-Test is not forwarded properly.
Does anyone have any ideas?
The Component Code
<template>
<a class="btn">
<slot></slot>
</a>
</template>
<script>
import {version } from "vue";
export default {
name: 'xn-button',
props: {
variant: {
default: 'normal',
type: String
}
},
setup(props, context) {
console.log(`Vue: ${version}`)
console.log(props)
console.log(props.variant)
},
}
</script>
<style></style>
Usage as Part of Vue Library:
<xn-button variant="Vue-Test">Test1</xn-button>
Usage as WebComponent:
<xn-button variant="wc-Test">Test2</xn-button>
Console output:
image

reference assets with generated style data bind

I'm using the vue-cli 3 and want to generate background-image paths. Therefore I'm using the style data bind within an v-for-loop.
the component:
<template>
<ul>
<li v-for="(tool, index) in tools" :key="index">
</li>
</ul>
</template>
<script>
export default {
props: {
tools: Array,
}
}
</script>
The images are in this folder: src/assets/icons/xxx.svg
The problem is, that the generated image path seems to be incorrect but I can't find the mistake.
That's because webpack is not parsing any path inside the HTML(he's not that smart -yet-).
Anyway, you could trick webpack to get the URL for you. Doesn't look really like the best solution to me, but will answer your question.
Just create a computed property for a new list containing all the tools with their image paths. The trick is letting webpack parse the URL path for you in that object, then reference it in your HTML
Note: I supposed each item in the tools array is an unique string.
<template>
<ul>
<li v-for="tool in items" :key="tool.name">
</li>
</ul>
</template>
<script>
export default {
props: {
tools: Array,
},
computed: {
items () {
return this.tools.map(tool => {
return {
name: tool,
// the trick is letting webpack parse the URL path for you
img: require(`#/assets/icons/${tool}.svg`)
}
})
}
}
}
</script>

Event handling dynamic created buttons in Vue.js v-for loop

I have a v-for loop where a button is created for each iteration. I'm trying to make a toggle handler where clicking the button will toggle the color of the button. But since the buttons are dynamically created, all of their colors are changing ....
<div class="pets" v-for="pet in pets" :key="pet.id">
<button class="favorite" v-on:click="toggle">
<i v-bind:class="[{ 'red' : favorited }, 'material-icons']">favorite</i>
</button>
</div>
The pets array is being filled with a http call. My script looks like this:
<script>
export default {
name: 'home',
data() {
return {
pets: [],
favorited: false
}
},
methods: {
toggle: function() {
this.favorited = !this.favorited;
}
},
}
The Style tag just changes the color
<style scoped>
.red {
color: red;
}
Essentially, I'm trying to created a favorite button where you can toggle favoriting an object from the array pets. I know why my method would activate all my buttons. Since favorited is not unique to a button and coming from the data. So when favorited = true, the class 'red' is bound to all my buttons. My question is how do I bind the class 'red' to just the button I click on? I'm relatively new to Vue and this is driving me nuts lol! Someone please tell me how I can fix this.
Add a favorited property to your pet objects in the pets array. Then use that property.
<div class="pets" v-for="pet in pets" :key="pet.id">
<button class="favorite" v-on:click="pet.favorited = !pet.favorited">
<i v-bind:class="[{ 'red' : pet.favorited }, 'material-icons']">favorite</i>
</button>
</div>
Example.
If you didn't want to modify the pet object, then here is an alternate way.

Polymer communication between elements

I want to achieve communication between child parent with Polymer element.
Here my index.html
<proto-receiver data="message">
<proto-element data="message"></proto-element>
</proto-receiver>
Both element have their respective "data" property
properties: {
data: {
value: 'my-data',
notify: true,
}
},
In proto-receiver, which is the parent I update "data" by handling simple click
<template>
<span on-tap="onClick">proto receiver: {{data}}</span>
<content></content>
</template>
onClick: function () {
this.data = 'new-message';
},
I want the change to be propagate to the child element as well, as it mentioned here.
I achieve this by passing a setter in my child element and called it like this. Which is, I guess, not the way it should be done.
Polymer.Base.$$('body').querySelector('proto-element').setData(this.data);
What I'm doing wrong
Thanks
UPDATE:
For those coming here. The proper way of doing this is by using Events.
Polymer 1.x
this.fire('kick', {kicked: true});
Polymer 2.x (simple javascript)
this.dispatchEvent(new CustomEvent('kick', {detail: {kicked: true}}));
In both case the receiver should implement the regular addEventListener
document.querySelector('x-custom').addEventListener('kick', function (e) {
console.log(e.detail.kicked); // true
})
To provide a concrete example to Scott Miles' comments, if you can wrap your parent and child elements in a Polymer template (such as dom-bind or as children to yet another Polymer element), then you can handle this declaratively. Check out the mediator pattern.
parent element:
<dom-module id="parent-el">
<template>
<button on-tap="onTap">set message from parent-el</button>
<p>parent-el.message: {{message}}</p>
<content></content>
</template>
<script>
Polymer({
is: 'parent-el',
properties: {
message: {
type: String,
notify: true
}
},
onTap: function() {
this.message = 'this was set from parent-el';
}
});
</script>
</dom-module>
child element:
<dom-module id="child-el">
<template>
<p>child-el.message: {{message}}</p>
</template>
<script>
Polymer({
is: 'child-el',
properties: {
message: {
type: String,
notify: true
}
}
});
</script>
</dom-module>
index.html:
<template is="dom-bind" id="app">
<parent-el message="{{message}}">
<child-el message="{{message}}"></child-el>
</parent-el>
</template>
<script>
(function(document) {
var app = document.querySelector('#app');
app.message = 'this was set from index.html script';
}) (document);
</script>
JS Bin
I was facing same issue and got solution for it and fixed it as below
this.fire('iron-signal', {name: 'hello', data: null});
You can refer this iron-signals you will get the solution which you are looking for its basically event fire from any element to another
Hope this will help you
Polymer iron signals

Resources