on click button within v-for change this attribute vue js - javascript

I am trying to create an 'add friend' button like facebook. When click this 'add friend' button it changes to 'friend request sent' button. I have tried following approach which doesn't work:
html:
<div v-for="(friend, index) in friends">
<div v-if="friend.sentRequest">
<button class='disabled'>Friend request sent</button>
</div>
<div v-else>
<button #click="addFriend(friend)">Add friend</button>
</div>
</div>
script:
<script>
export default {
data() {
return {
friends: [],
}
},
methods: {
addFriend(friend) {
friend.sentRequest = true;
}
}
}
</script>
I need help to fix this. Thanks in advance.

When you want to define property for existing object you should to use this.$set(target, key, value).
In this case use:
methods: {
addFriend(friend) {
this.$set(friend, 'sentRequest', true);
}
}

Related

Display dropdowns dynamically in one component

I want to have multiple dropdowns in one component using one variable to display or not and also clicking away from their div to close them:
<div class="dropdown">
<button #click.prevent="isOpen = !isOpen"></button>
<div v-show="isOpen">Content</div>
</div>
// second dropdown in same component
<div class="dropdown">
<button #click.prevent="isOpen = !isOpen"></button>
<div v-show="isOpen">Content</div>
</div>
data() {
return {
isOpen: false
}
},
watch: {
isOpen(isOpen) {
if(isOpen) {
document.addEventListener('click', this.closeIfClickedOutside)
}
}
},
methods: {
closeIfClickedOutside(event){
if(! event.target.closest('.dropdown')){
this.isOpen = false;
}
}
}
But now when I click one dropdown menu it displays both of them. I am kinda new to vue and cant find way to solve this
To use just one variable for this, the variable needs to identify which dropdown is open, so it can't be a Boolean. I suggest storing the index (e.g., a number) in the variable, and conditionally render the selected dropdown by the index:
Declare a data property to store the selected index:
export default {
data() {
return {
selectedIndex: null
}
}
}
Update closeIfClickedOutside() to clear the selected index, thereby closing the dropdowns:
export default {
methods: {
closeIfClickedOutside() {
this.selectedIndex = null
}
}
}
In the template, update the click-handlers to set the selected index:
<button #click.stop="selectedIndex = 1">Open 1</button>
<button #click.stop="selectedIndex = 2">Open 2</button>
Also, update the v-show condition to render based on the index:
<div v-show="selectedIndex === 1">Content 1</div>
<div v-show="selectedIndex === 2">Content 2</div>
Also, don't use a watcher to install a click-handler on the document because we want to know about the outside-clicks when this component is rendered. It would be more appropriate to add the handler in the mounted hook, and then remove in the beforeDestroy hook:
export default {
mounted() {
document.addEventListener('click', this.closeIfClickedOutside)
},
beforeDestroy() {
document.removeEventListener('click', this.closeIfClickedOutside)
},
}
demo
Make an array and loop through it, much easier that way.
<template>
<div id="app">
<div class="dropdown" v-for="(drop, index) in dropData" :key="index">
<button #click="openDropdown(index);">{{ drop.title }}</button>
<div v-show="isOpen === index">{{ drop.content }}</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
isOpen: null,
dropData: [
{
title: "Hey",
content: "Hey it's content 1"
},
{
title: "Hey 2",
content: "Hey it's content 2"
},
{
title: "Hey 3",
content: "Hey it's content 3"
},
]
};
},
methods: {
openDropdown(idx){
if (this.isOpen === idx) {
this.isOpen = null;
} else {
this.isOpen = idx;
}
}
}
};
</script>

Automatically update the input field within a form, vue.js

I want to search for elements using an input field within a form. I am passing a value from a component to another and the content changes but only after pressing enter. Is there a way to automatically update the list, after every new letter is typed?
Here is my code:
Parent(App):
<template>
<div id="app">
<Header v-on:phrase-search="passPhrase" />
</div>
</template>
<script>
import Header from ...
export default {
name: "App",
components: {
Header,
},
data() {
return {
posts: [],
searchedPhrase: ""
};
}
computed: {
filteredPosts() {
let temp_text = this.searchedPhrase;
temp_text.trim().toLowerCase();
return this.posts.filter(post => {
return post.name.toLowerCase().match(temp_text);
});
}
},
methods: {
passPhrase(phrase) {
this.searchedPhrase = phrase;
}
}
};
</script>
Child(Header):
<template>
<div class="child">
<p>Search:</p>
<form #submit.prevent="phrasePassed">
<input type="text" v-model="phrase" />
</form>
</div>
</template>
<script>
export default {
name: "search",
data() {
return {
phrase: ""
};
},
methods: {
phrasePassed() {
this.$emit("phrase-search", this.phrase);
}
}
};
</script>
passPhrase() brings the value from the child to the parent and then filteredPosts() find appropriate elements. I suspect that the form might be guilty of this issue but I do not know precisely how to get rid of it and still be able to pass the value to the parent.
Thanks
in the child you use submit event which called on enter. you should use #input on the input itself. and btw you didnt need even to declare pharse in the data because you didnt use it in the child. you just pass it up
you it like so
<template>
<div class="child">
<p>Search:</p>
<form>
<input type="text" #input="phrasePassed">
</form>
</div>
</template>
<script>
export default {
name: "search",
methods: {
phrasePassed(e) {
this.$emit("phrase-search", e.target.value);
}
}
};
</script>

onclick event not firing in vue.js

I have the following Vue.js template :
<script type="text/x-template" id="ti-page-inquire">
<div>
<h3 class="mdc-typography--headline3">{{page.name}}</h3>
<ti-button v-bind:button="page.button" v-on:click="onSubmit"></ti-button>
</div>
</script>
<script type="text/x-template" id="ti-button">
<button class="mdc-button mdc-button--raised" v-bind:title="button.name">{{button.name}}</button>
</script>
script
Vue.component('ti-page-inquire', {
props: ['page'],
template: '#ti-page-inquire',
methods : {
onSubmit : function() {
alert(1);
}
}
});
Vue.component('ti-button', {
props: ['button'],
template: '#ti-button',
mounted: function () {
// ripple on button
mdc.ripple.MDCRipple.attachTo(this.$el);
}
});
when I click on my custom button, nothing happens. I think it's because its looking for onSubmit in the ti-button component, but how do I get it to look in the ti-page-inquire component?
Components are black boxes you should catch all events inside it and emit them to the outer world.
Fiddle example
Vue.component('ti-button', {
props: ['button'],
template: '#ti-button',
mounted: function () {
// ripple on button
mdc.ripple.MDCRipple.attachTo(this.$el);
},
methods: {
buttonClicked: function() {
this.$emit('button-clicked');
}
}
});
<script type="text/x-template" id="ti-page-inquire">
<div>
<h3 class="mdc-typography--headline3">{{page.name}}</h3>
<ti-button v-bind:button="page.button" v-on:button-clicked="onSubmit"></ti-button>
</div>
</script>
<script type="text/x-template" id="ti-button">
<button class="mdc-button mdc-button--raised" v-bind:title="button.name" #clicked="buttonClicked">{{button.name}}</button>
</script>
This might be because you need to listen for a native click event. So you need to use the .native modifier ..
<ti-button v-bind:button="page.button" v-on:click.native="onSubmit"></ti-button>
This will only work if the button is the root element of your ti-button component. Otherwise you'll have to pass your event listeners to your button in the ti-button component like this ..
<button v-on="$listeners" ...> ... </button>
Try to emit an event from ti-button component to the parent one by using this.$emit function :
Vue.component('ti-button', {
props: ['name'],
template: '#vButton',
data: {
name: 'hi'
},
methods: {
submit() {
this.$emit('submit')
}
}
});
<template id="vButton">
<button v-bind:title="name" #click="submit">{{name}}</button>
</template>
the emitted event submit it called in the parent component like v-on:submit="onSubmit" and handled using onSubmit method:
<script type="text/x-template" id="ti-page-inquire">
<div>
<h3 class="mdc-typography--headline3">{{page.name}}</h3>
<ti-button v-bind:button="page.button" v-on:submit="onSubmit"></ti-button>
</div>
</script>
Vue.component('ti-page-inquire', {
props: ['page'],
template: '#ti-page-inquire',
methods : {
onSubmit : function() {
alert(1);
}
}
});
Sometimes you need also to emit some parameters, so you could do it like :
this.$emit('submit',params)
params could be of any type

Handle key press by function in VueJs

in my component I am using VueStrap's modal like this:
<template>
<modal-window v-model="show" v-on:keyup="keyHandler($event)" #ok="submit()" #cancel="cancel()" #closed="close()" ... >
...
</modal-window>
...
</template>
<script>
...
methods: {
keyHandler (event) {
console.log(event);
}
},...
</script>
I want handle key press when that modal is opened and ensure submit modal when enter pressed or close modal when esc pressed.
I added custom function keyHandler which is unfortunately never fired. Can you tell me how to fix code to handle key press in that function? Or when is better way how to close and submit vue strap modal I will be grateful for advice. Thank you.
You can attach your event handler to window, that way you can receive all key events and act accordingly depending on your modal's state:
Vue.component('modal', {
template: '<div>test modal</div>',
});
new Vue({
el: "#app",
created() {
window.addEventListener('keydown', (e) => {
if (e.key == 'Escape') {
this.showModal = !this.showModal;
}
});
},
data: {
showModal: true
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app">
<modal v-show="showModal"></modal>
</div>
easiest way
<input v-on:keyup.13="whatkey()" type="text"> <br>
looks for if the enter key is pressed then fires a method called whatkey.
Alternatively, you may want to consider using the v-hotkey directive for key input in Vue (docs, github). This will keep your code relatively clean and simple if you must consider several different key inputs.
1.  Install it:
npm i --save v-hotkey
 Have Vue 'use' it:
import VueHotkey from "v-hotkey";
Vue.use(VueHotkey);
3.  Apply it to your Vue components, like so:
<template>
<modal-window ... v-hotkey="keymap">
...
</modal-window>
</template>
<script>
...
data() {
return {
showModal: false
};
},
computed: {
keymap() {
return {
"esc": this.toggleModal
};
}
},
methods: {
toggleModal() {
this.showModal = !this.showModal;
}
}
</script>

Vue event handler on dynamically inserted string does not work

This is my code:
<template>
<div>
<div v-html="data"></div> <button v-on:click="replace">Click Me to replace div contents</button>
</div>
</template>
<script>
export default {
data() {
return {
data: "I will be replaced once you click on button"
}
},
methods: {
clickMe() {
alert("worked");
},
replace(){
this.data = "Why does click me not work? It is loaded from server via ajax <a href v-on:click.prevent='clickMe'>Click Me</a>";
}
}
};
</script>
Here if I click on Click Me to replace div contents the content is replaced but the event handler clickMe does not fire. This data would come from server and I need to compile this string and use it from within the Vue's context so Vue can handle events etc.
How can I have the dynamic string downloaded from server work? I am using Vue 2.
Since v-html isn't compiled you will have to create a mini component like this to get around the issue:
new Vue({
el: '#app',
data () {
return {
data: ``
}
},
computed: {
compiledData () {
return {
template: `<p>${this.data}</p>`
}
}
},
methods: {
replace () {
this.data = `Now click on me <a href='#' #click.prevent='alert("yo")'> here </a>`
}
}
})
<script src="https://unpkg.com/vue#2.5.3/dist/vue.min.js"></script>
<div id="app">
<component :is="compiledData" ></component>
<button v-on:click="replace">Click Me to replace div contents</button>
</div>
The above code compiles the string content and thus you can run/execute the function as intended
Other solution using Vue components (codepen):
<script src="https://unpkg.com/vue"></script>
<div id="app">
<div id="someId"></div> <button v-on:click="replace">Click Me to replace div contents</button>
<component :is="currentView"></component>
</div>
<script>
let app = new Vue({
el: '#app',
data: {
currentView: null
},
methods:{
replace: function(){
var templateFromServer = getTemplate();
var comp=Vue.component('template-from-server', {
template: templateFromServer,
methods:{
clickMe:function (){
console.log("click");
}
}
});
this.currentView = comp;
}
}
});
function getTemplate(){
return "<a href v-on:click.prevent='clickMe'>Click Me</a>"
}
</script>
v-html is not compiled as a Vue template. From the docs:
Note that the contents are inserted as plain HTML - they will not be compiled as Vue templates. If you find yourself trying to compose templates using v-html, try to rethink the solution by using components instead.
see: https://v2.vuejs.org/v2/api/#v-html
You can not render VueJS code from a html string.
You can solve this issue by using v-if
<div>
<div v-if="data">I will be replaced once you click on button</div>
<div v-else>Why does click me not work? It is loaded from server via ajax <a href #click.prevent='clickMe'>Click Me</a></div>
<button #click="replace">Click Me to replace div contents</button>
</div>
<script>
export default {
data() {
return {
data: true
}
},
methods: {
clickMe() {
alert("worked");
},
replace(){
this.data = !this.data;
}
}
};
You can call normal javascript function from string but not vuejs function so onclick event would also work.

Categories

Resources