I'm used to jquery and I'm rewriting my code to Vue. Need a little help with this basic onclick event.
Original html:
<div id="main">
<div id="right-nav" onclick="closeRightNav();"></div>
<i onclick="openRightNav();"></i>
</div>
Original javascript:
function openRightNav() {
$("#right-nav").width("100%");
}
function closeRightNav() {
$("#right-nav").width("0");
}
This is where I'm currently at:
New html:
<div id="main">
<div id="right-nav" #click="closeRightNav"></div>
<i #click="openRightNav"></i>
</div>
New javascript using Vue:
var app = new Vue({
el: '#main',
data: {
width: '100%'
},
methods: {
openRightNav() {
$("#right-nav").width("100%"); // not sure how to write this part in Vue?
}
}
});
How do I write this correctly in Vue?
Ok. You can approach this from multiple angles. I like to use v-show or v-if directives like so:
<template>
<div>
<div v-if="rightOpen" id="right-div" style="width:100%">Right Div</div>
<a #click="rightOpen = ! rightOpen">Toggle</a> // this will actually toggle right sidebar open and close
</div>
</template>
<script>
export default {
mounted() {
},
data: function() {
return {
rightOpen: false
}
}
}
</script>
Now you could use v-show instead of v-if difference is v-show element will be rendered on the page but not shown and v-if will not render element.
You could also use v-class like so
...
<div :class="{ 'someClass': !rightOpen,'sidebar-opened': rightOpen}" id="right-div">Right Div</div>
....
In this example someClass will be the one loaded when component is rendered, lets say width: 0 will be in that class. sidebar-opened class should contain width: 100%. Everything else stays the same from previous example.
in openRightNav you change your data and don't manipulate the DOM directly. Just say in your method: this.width = 100.
in your html then you need to bind your width attribute like this:
<div id="right-nav" #click="closeRightNav" v-bind:style="{'width': width + '%'}"></div>
don't forget to set your default value for width to 0.
in closeRightNav you would only write: this.width = 0
Thanks to Yousef Kama here's how I ended up doing it:
New html:
<div id="main">
<div id="right-nav" #click="closeRightNav" v-bind:style="{'width': width + '%'}"></div>
<i #click="openRightNav"></i>
</div>
New Vue javascript:
var app = new Vue({
el: '#main',
data: {
width: 0
},
methods: {
openRightNav() {
this.width = 100;
},
closeRightNav() {
this.width = 0;
}
}
});
Related
I'm trying to create a custom slider to emulate horizontal scroll behavior of a specific element. the slider is implemented as a stand alone component and the selector for the element is passed to the slider component as a property:
<template>
<div class="slidercontainer">
<h1>{{contentWidth}}</h1>
<button class="right-scroll"></button>
<input
type="range"
min="1"
:max="contentWidth"
value="1"
class="slider"
v-on:input="handleScroll"
>
<button class="left-scroll"></button>
</div>
</template>
<script>
export default {
props: ["el"],
data() {
return {
scrollLeft: 0
};
},
methods: {
handleScroll($event) {
this.content.scrollLeft = this.contentWidth - $event.target.value;
}
},
computed: {
content() {
return document.querySelector(this.el);
},
contentWidth() {
return this.content.scrollWidth - this.content.clientWidth;
}
}
};
</script>
the problem though, is that content wont update and always return 0. when first loaded the app is waiting for some data from the server and thats why when mounted the contentWidth equals 0, but i thought returning content as a computed property should take care of that, but nothing happens after the content is injected with some new...well - content :)
any idea how to solve this?
How do you toggle a class in vue.js for list rendered elements? This question is an extension on this well answered question. I want to be able to toggle each element individually as well as toggle them all. I have attempted
a solution with the below code but it feels fragile and doesn't seem to work.
A different solution would be to use a single variable to toggle all elements and then each element has a local variable that can be toggled on and off but no idea how to implement that..
// html element
<button v-on:click="toggleAll"></button>
<div v-for="(item, i) in dynamicItems" :key=i
v-bind:class="{ active: showItem }"
v-on:click="showItem[i] = !showItem[i]">
</div>
//in vue.js app
//dynamicItems and showItem will be populated based on API response
data: {
dynamicItems: [],
showItem: boolean[] = [],
showAll: boolean = false;
},
methods: {
toggleAll(){
this.showAll = !this.showAll;
this.showItem.forEach(item => item = this.showAll);
}
}
Here is the small example to acheive you want. This is just a alternative not exact copy of your code.
var app = new Vue({
el:'#app',
data: {
dynamicItems: [
{id:1,name:'Niklesh',selected:false},
{id:2,name:'Raut',selected:false}
],
selectedAll:false,
},
methods: {
toggleAll(){
for(let i in this.dynamicItems){
this.dynamicItems[i].selected = this.selectedAll;
}
}
}
});
.active{
color:blue;
font-size:20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.9/vue.js"></script>
<div id="app">
<template>
<input type="checkbox" v-model="selectedAll" #change="toggleAll"> Toggle All
<div v-for="(item, i) in dynamicItems">
<div :class='{active:item.selected}'><input type="checkbox" v-model="item.selected">Id : {{item.id}}, Name: {{item.name}}</div>
</div>
{{dynamicItems}}
</template>
</div>
I think all you need to do is this
v-bind:class="{ active: showItem || showAll }"
and remove the last line from toggleAll
You also need to use Vue.set when updating array values, as array elements aren't reactive.
Suppose I want to use the components to make a list that would disappear if I click on it, and use the transition-group to do the animation part.
The following code can perform well:
HTML:
<transition-group name="testanim">
<p key="1" v-if='open1' #click='open1 = false'>Can You See Me?</p>
<p key="2" v-if='open2' #click='open2 = false'>Can You See Me?</p>
</transition-group>
CSS:
.testanim-enter-active, .testanim-leave-active {
transition: all .5s;
}
.testanim-enter, .testanim-leave-to {
transform: translateX(1rem);
opacity: 0;
}
.testanim-leave-active {
position: absolute;
}
.testanim-move {
transition: all .5s;
}
open1 and open2 are defined in data in Vue.js.
However, the following code would not perform the animation at all.
HTML:
<transition-group name="testanim">
<test-sth key="1"></test-sth>
<test-sth key="2"></test-sth>
</transition-group>
CSS: the same with above
JavaScript:
Vue.component ("test-sth", {
template: "<p v-if='open' #click='open = !open'>Can You See Me?</p>",
data: function () {
return {
open: true,
}
}
})
So the problem is that how I can animate the components inside the transition-group. I've searched for a few hours but did not find some question or documents related to it.
Update:
The key problem is that the animation in the former example that the second sentence move upwards smoothly when the first sentense disappear do not show in the latter one. Although I may put the transition inside the template, That do not solve the problem.
Should I write the whole transition-groupinside the template, or something else...?
When using Vue transitions, for internal reasons, the transition/transition-group components must be in the same template as the state that's being toggled.
Also, Vue components require that there always be a single root element for a component. A v-if breaks this rule because it gives the possibility of the element not being there, if the v-if happens to be false.
To solve your issue, move the transitioning to the test-sth component. Since it manages its own toggling, it should manage its own transitioning as well.
Vue.component("test-sth", {
template: `
<transition name='testanim'>
<p v-if='open' #click='open = !open'>Can You See Me?</p>
</transition>
`,
data: () => ({
open: true,
}),
})
new Vue({
el: "#app",
template: `
<div>
<test-sth></test-sth>
<test-sth></test-sth>
</div>
`,
})
See this fiddle for a working example.
I am novice in VueJs and As I am trying to implement the basic toggle class functionality using v-bind property of VueJs in my Laravel project. I am not getting the value of variable className while rendering of the page. Please guide me where I am doing wrong. The code is given below:
<div id="root">
<button type="button" v-bind:class="{'className':isLoading}" v-on:click="toggleClass">Toggle Me</button>
</div>
JavaScript is:
<script>
var app = new Vue({
el: '#root',
data: {
className:"color-red",
isLoading:false
},
methods:{
toggleClass(){
this.isLoading=true;
this.className="color-blue";
}
}
})
</script>
Style is:
<style>
.color-red{
background-color:red;
}
.color-blue{
background-color:blue;
}
</style>
You're mixing your approaches slightly. The main issue is in v-bind:class="{'className':isLoading}". This directive, the way you wrote it, toggles a class with the name "className" (literally that, not the value of the variable className) to your element if isLoading is true. If it's false, it doesn't assign any class.
Looking at your code, it seems you're actually trying to set two different classes depending on what the value of isLoading is. The easiest way to do this would be to use v-bind:class="isLoading ? 'color-red' : 'color-blue". Take a look at a working example here.
An even better solution that doesn't pollute your template with logic is to move that expression to a computed property, like this.
You can not have className as well as a variable name, as vue expects it as actual CSS class, documentation suggests one more way, have class object like following:
<script>
var app = new Vue({
el: '#root',
data: {
classObj:{ "color-red" : true } ,
isLoading:false
},
methods:{
toggleClass(){
this.isLoading=true;
this.classObj = { "color-blue" : true};
}
}
})
</script>
<div id="root">
<button type="button" v-bind:class="classObj" v-on:click="toggleClass">Toggle Me</button>
</div>
I've got two problems here. The first is that I can't get the star rendered properly. I can do it if I change the value in the data() function but if I want to do it in a function callback way, it doesn't work (see comments below). What's going wrong here? Does it have something to do with Vue's lifecycle?
The second one is that I want to submit the star-rate and the content of the textarea and when I refresh the page, the content should be rendered on the page and replace the <textarea></textarea> what can I do?
I want to make a JSFiddle here but I don't know how to make it in Vue's single-file component, really appreciate your help.
<div class="order-comment">
<ul class="list-wrap">
<li>
<span class="comment-label">rateA</span>
<star-rating :data="dimensionA"></star-rating>
</li>
</ul>
<div>
<h4 class="title">comment</h4>
<textarea class="content" v-model="content">
</textarea>
</div>
<mt-button type="primary" class="mt-button">submit</mt-button>
</div>
</template>
<script>
import starRating from 'components/starRating'
import dataService from 'services/dataService'
export default {
data () {
return {
dimensionA: '' //if I changed the value here the star rendered just fine.
}
},
components: {
starRating
},
methods: {
getComment (id) {
return dataService.getOrderCommentList(id).then(data => {
this.dimensionA = 1
})
}
},
created () {
this.getComment(1) // not working
}
}
</script>
What it seems is scope of this is not correct in your getComment method, you need changes like following:
methods: {
getComment (id) {
var self = this;
dataService.getOrderCommentList(id).then(data => {
self.dimensionA = 1
})
}
},
As you want to replace the <textarea> and render the content if present, you can use v-if for this, if content if available- show content else show <textarea>
<div>
<h4 class="title">comment</h4>
<span v-if="content> {{content}} </span>
<textarea v-else class="content" v-model="content">
</textarea>
</div>
See working fiddle here.
one more problem I have observed in your code is you are using dynamic props, but you have assigned the prop initially to the data variable value in star-rating component, but you are not checking future changes in the prop. One way to solve this, assuming you have some other usage of value variable is putting following watch:
watch:{
data: function(newVal){
this.value = newVal
}
}
see updated fiddle.