In a blog app, I'd like to show/hide comments of each post inside a loop of posts. I know how to show the div containing the comments by setting a showComments on the fly:
this.$set(post, 'showComments', true) ;
But I don't know how to hide the post's comments when the div is already open. What I tried is this:
if (this.$get(post, 'showComments')==true) {
this.$set(post, 'showComments', false) ;
return
}
The code above thoes not work and I get this error:
Uncaught TypeError: this.$get is not a function
So I'm wondering how can I acheive this functionaliry.
You should be able to simply read the dynamic property and reapply the value.
new Vue({
el: '#app',
data() {
return {
posts: [
{ content: 'Post #1' },
{ content: 'Post #2' },
{ content: 'Post #3' }
]
}
},
methods: {
toggleComment(post) {
if ('showComment' in post) {
post.showComment = !post.showComment;
}
else {
this.$set(post, 'showComment', true);
}
}
}
})
.post {
background-color: lightgreen;
margin: 10px 0;
padding: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class="post" v-for="(post, index) in posts" :key="index">
{{post.content}}
<p v-if="post.showComment">
Hidden comments.
</p>
<button #click="toggleComment(post)">Toggle comment</button>
</div>
</div>
Use property name to get property value
if ( typeof this.post.showComments !== 'undefined' && this.post.showComments ) {
Vue.set(post, 'showComments', false);
return;
}
Also note that you should try to avoid using this.$set because it was deprecated due to conflicts with other libraries. Consider using Vue.set instead.
https://v2.vuejs.org/v2/api/#Vue-set
Related
I'm having a weird proplem that I can't understand
I have a registration form, and on any input I'm executing the method on blur;
<input class='form-control' placeholder='Username' #blur="watchVal" v-model="username">
Method
watchVal : function(){
if(this.username == ""){
this.errors['username'].push('Username is empty');
}
}
Data:
data: function(){
return {
username: "",
errors: {
'username' : []
}
}
}
When I blur without writing any value, nothing is added to this.errors['username'], unless I type a letter in any field.
I've also tried to make validation on submit, but found same problem that no error is added to the array unless I type in any input,
Can anyone explain to me what I am doing wrong??
I faced similar issue. How I solved this.
your data:
data: function(){
return {
username: "",
errors: {}
}
}
your Method
watchVal (key) {
let errors = this.errors
if (this[key] === '') {
errors[key].push('Emai empty')
}
this.errors = errors
}
your HTML
<input class='form-control' placeholder='Username' #blur="watchVal('username')" v-model="username">
<p>{{errors['username']}}</p>
You must display error variable in your HTML template then it will be solved.
Your source is working. See in full mode if you cannot see error messages.
new Vue({
el: "#app",
data: function() {
return {
username: '',
errors: {
username: []
}
}
},
methods: {
watchVal : function(){
if(this.username == ""){
this.errors['username'].push('Username is empty');
}
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input class='form-control' placeholder='Username' #blur="watchVal" v-model="username">
<p class="text-sm text-danger" v-for="(error, index) in errors.username" :key="index">
{{ error }}
</p>
</div>
Currently I have something along the lines of:
data(){
return {
summonerMatches: '',
}
},
computed: {
highestMultikill(){
let highestMultikill = 0;
for (let match of this.summonerMatches.matches) {
if (highestMultikill < match.mainParticipant.stats.largestMultiKill) {
highestMultikill = match.mainParticipant.stats.largestMultiKill
}
}
return highestMultikill
},
}
When I use this computed property in my template like this:
<p>{{ highestMultikill }}</p>
I render a number that symbolizes the highest multi kill of a user. Now I would like to somehow add a class to that <p> element based on the highestMultiKill. If highestMultiKill = 1, set a class that changes the color to blue, if highestMultiKill = 5, set a class that changes the color to red, etc.
I'm not sure how to do that with my current setup. I was thinking about having the computed property return an entire different <p>element based on the highestMultikill variable like this:
if (highestMultiKill == 1) {
return <p class='blue'>highestMultiKill</p>
} else {
return a different `<p>` element with a different class
}
Is this the correct way to do this?
Here's an example with more complex logic and BEM-inspired class names:
<template>
<p :class="klass">{{ highestMultiKill }}</p>
</template>
<script>
export default {
data() {
return { highestMultiKill: 0 };
},
computed: {
klass() {
return {
score: true, // Always have a score class
'score--low': this.highestMultiKill <= 2,
'score--medium': this.highestMultiKill > 2 && this.highestMultiKill <= 5,
'score--high': this.highestMultiKill > 5 && this.highestMultiKill <= 10,
'score--blood-lust': this.highestMultiKill > 10,
};
},
},
};
</script>
<style scoped>
.score {
line-height: 2;
}
.score--low {
color: blue;
}
.score--medium {
color: pink;
}
.score--medium {
color: red;
}
.score--medium {
color: darkred;
}
</style>
The idea is that the p will have a class like: score score--medium
You can use a conditional class like this:
<p :class="highestMultiKill == 1 ? 'blue' : 'otherClass'">highestMultiKill</p>
See the full documentation on conditional classes in Vue.js here.
By the way you should not initialize summonerMatches with a string if it's actually an object with reactive properties.
The best way to do this, if you have complex logic involving many variables and or classes, is something like this:
<p :class="killClass"></div>
computed: {
killClass: function () {
return {
highestMultiKill >= 3 ? 'blue' : 'red'
}
}
}
If you have more classes/states then use an if statement instead.
This sample can be of use:
<html>
<head>
</head>
<body>
<div id="container">
<p v-if="this.computedValue==0" style="color:blue;" >content0</p>
<p v-if="this.computedValue==1" style="color:red;" >content1</p>
<p v-if="this.computedValue==2" style="color:green;" >content2</p>
<p v-if="this.computedValue==3" style="color:yellow;" >content3</p>
<p v-if="this.computedValue==4" style="color:pink;" >content4</p>
<p v-if="this.computedValue==5" style="color:aquamarine;" >content5</p>
<p v-if="this.computedValue==6" style="color:blueviolet;" >content6</p>
<p v-if="this.computedValue==7" style="color:brown;" >content7</p>
<p v-if="this.computedValue==8" style="color:chartreuse;" >content8</p>
<p v-else>content9</p>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/0.11.10/vue.min.js"></script>
<script>
new Vue({
el: '#container',
data: {
value: '',
},
computed: {
computedValue: function() {
var styleNumber = 0;/*return a number based on a logic*/
return styleNumber;
}
}
});
</script>
</body>
</html>
I'm trying to dynamically change the css of some text in a span when a checkbox is checked using v-on:change but it is not changing and I can't figure out why.
The boolean data property "bolden" does change on-click (checked with console.log) but the class does not. the class does not even appear in the 'span' element tag when checked on devtools in chrome
here is a jsfiddle link of a piece of the code: https://jsfiddle.net/rL0fzv7s/
<script src="https://unpkg.com/vue"></script>
<div id="app">
<input type="checkbox" value="food" v-model="blog.categories" v-on:change="bolden = !bolden">
<span v-bind:class="bold">food</span>
</div>
.bold {
font-weight: bolder;
font-size: 25px;
color: red;
}
new Vue({
el: '#app',
data: {
bolden: false,
blog: {
title: "",
content: "",
categories: []
}
},
computed: {
bold: function() {
return {
bolden: this.bolden
};
}
}
})
The reason why your styles are not showing even though the value of bolden is changed is that the class name that you are using is .bold but the class name that you are returning from the computed property is called .bolden.
Change your computed bold functions's return value to the following:
computed: {
bold: function() {
return {
bold: this.bolden // class
};
}
}
You should return bold class while this.bolden true. below is the corrected code. Fiddle : https://jsfiddle.net/62mewykL/
bold: function() {
return {
bold: this.bolden
};
}
OR
Simply use v-bind:class="{'bold' : this.bolden}" in HTML
Frequently, I want to render a div (or other element) only if it has content. This means repeating the reference to the content in the tag, and in v-if, like this...
<div v-if="store.sometimesIWillBeEmpty">{{store.sometimesIWillBeEmpty}}</div>
With custom directives, I want to create a directive, v-fill, that behaves just like the code above, but with simplified syntax...
<div v-fill="store.sometimesIWillBeEmpty"></div>
updated The following works when message is not empty. What do I set or clear to render nothing when message is empty?
var store = {message: "hello cobber"}
Vue.directive('fill',
function (el, binding, vnode) {
if(binding.value)
el.innerHTML = binding.value
else
el = null
}
);
new Vue({
el: '#fill-example',
data: {
store: store
}
})
I'm one line away. Here's my fiddle. Anyone have any ideas?
It is possible to make a straightforward component to do what you want. A directive requires a bit more manipulation to be able to remove the element and put it back in the right place.
const vm = new Vue({
el: '#fill-example',
data: {
empty: '',
notEmpty: 'I have content'
},
components: {
renderMaybe: {
props: ['value'],
template: `<div v-if="value" class="boxy">{{value}}</div>`
}
},
directives: {
fill: {
bind(el, binding) {
Vue.nextTick(() => {
el.vFillMarkerNode = document.createComment('');
el.parentNode.insertBefore(el.vFillMarkerNode, el.nextSibling);
if (binding.value) {
el.textContent = binding.value;
} else {
el.parentNode.removeChild(el);
}
});
},
update(el, binding) {
if (binding.value) {
el.vFillMarkerNode.parentNode.insertBefore(el, el.vFillMarkerNode);
el.textContent = binding.value;
} else {
if (el.parentNode) {
el.parentNode.removeChild(el);
}
}
}
}
}
});
setTimeout(() => {
vm.empty = "Now I have content, too.";
}, 1500);
.boxy {
border: thin solid black;
padding: 1em;
}
<script src="//unpkg.com/vue#latest/dist/vue.js"></script>
<div id="fill-example">
Components:
<render-maybe :value="empty"></render-maybe>
<render-maybe :value="notEmpty"></render-maybe>
Directives:
<div class="boxy" v-fill="empty"></div>
<div class="boxy" v-fill="notEmpty"></div>
</div>
I don't have much knowledge about child and parent component and i am just simply trying to change value in child then emit then value to parent so i can show somewhere, But it looks like not working, Not emiting the value.
This should change to : Hello from child
{{ message }} From Parent
Can anyone look this code and tell me what is mistake ?
Vue.component('child1', {
template: '<p #click="runMe">{{ display }}</p>',
props: ['display'],
data: {
display: ''
},
methods: {
runMe() {
this.display = "Hello from child"
this.$emit("changeMessage", this.display)
}
}
})
new Vue({
el: "#app",
data: {
message: "Hello 2"
},
methods: {
messageRun() {
this.message = "Change By"
}
}
})
.btnMain {
display: block;
background: #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.2/vue.min.js"></script>
<div id="app">
<button #click="messageRun" class="btnMain">Click Here</button>
<child1 :display="message" #changeMessage="message = $event"></child1>
<hr>
{{ message }} From Parent
</div>
In your code you have this where you emit:
this.$emit("changeMessage", this.display)
Change to :
this.$emit("newmessage", this.display)
I mean use lower case single word, if you use camelCase vue convert it to change-message but this is not acceptable by attribute, I tried this on your code.
After change this do this :
<child1 :display="message" #newmessage="message = $event"></child1>