Can't change button text inside setTimeout function - vue.js

I have got follow Vue-js App:
<html>
<head>
<script src="https://cdn.jsdelivr.net/vue/1.0.26/vue.min.js"></script>
<style>
.mydiv
{
border: 1px black dashed;
font-size: 2em;
}
</style>
<script>
var App = null; // it's global because function behind will overwrite it's with Vue App instance
window.onload = function()
{
new Vue(
{
el: '#app',
data:
{
btntext: "OK"
},
methods:
{
change: function()
{
this.btntext = "cancel";
setTimeout(function() {console.log("test"); this.btntext = "text changed";},1000);
}
}
})
}
</script>
</head>
<body>
<div id="app">
<div class="mydiv">
<button v-on:click="change">{{btntext}}</button>
</div>
</div>
</body>
</html>
After running I am getting "test" on console, but button do not change it's text to text changed. Why?

The function given to setTimeout does not have the same "this" as your Vue. You could use the bind function:
new Vue({
el: '#app',
data: {
btntext: "OK"
},
methods: {
change: function () {
this.btntext = "cancel";
setTimeout(function () {
console.log("test");
this.btntext = "text changed";
}.bind(this), 1000);
}
}
})
.mydiv{
border: 1px black dashed;
font-size: 2em;
}
<script src="https://cdn.jsdelivr.net/vue/1.0.26/vue.min.js"></script>
<div id="app">
<div class="mydiv">
<button v-on:click="change">{{btntext}}</button>
</div>
</div>

You have to understand the context of this keyword. When in setTimeout callback function, this refers to different object that this before it. To solve this issue, you should solve reference to this before the callback or if you're going to use ES2015, you can change function () {...} with arrow function () => {...}, which will automatically save reference to outer this and use it instead of actual this inside the function. But if you're going to use that, make sure it's supported across all your target browsers, or alternatively use a compiler to ES5, most popular of which is Babel.

Related

My html code can't reach my vue component

This is my second day with vue. I was already using webpack and vue-cli, but I would like to understand how to make everything working within one file. I developed a code which works well, but I would like to refactor the code to have a component which I could later use to generate screen full of color changing tiles.
I tried Vue.component('name', {}), but with no result, because in the console I'm seeing [Vue warn]: The "data" option should be a function that returns a per-instance value in component definitions. and [Vue warn]: Unknown custom element: <brick> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
This code works well:
<html>
<head>
<title>v pavle</title>
<script type="text/javascript" src="https://vuejs.org/js/vue.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/javascript">
var vm = new Vue({
el: "#app",
template:
'<div v-bind:style="styleobj" v-on:mouseover="changebgcolor" v-on:mouseout="changebgcolor"></div>',
data: {
styleobj: {
width: "100px",
height: "100px",
backgroundColor: "white"
}
},
methods: {
changebgcolor: function() {
this.styleobj.backgroundColor = Math.floor(
Math.random() * 16777215
).toString(16);
}
}
});
</script>
</body>
</html>
And that code gives everything, but not what I want to see :(
<html>
<head>
<title>v pavle</title>
<script type="text/javascript" src="https://vuejs.org/js/vue.js"></script>
</head>
<body>
<div id="app">
<brick></brick>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app"
});
var brick = Vue.component("brick", {
template:
'<div v-bind:style="styleobj" v-on:mouseover="changebgcolor" v-on:mouseout="changebgcolor"></div>',
data: {
styleobj: {
width: "100px",
height: "100px",
backgroundColor: "white"
}
},
methods: {
changebgcolor: function() {
this.styleobj.backgroundColor = Math.floor(
Math.random() * 16777215
).toString(16);
}
}
});
</script>
</body>
</html>
It may seem easy for you, but after 7h spent, there is nothing more for me, but just ask you on SO
Okay I will answer your 2 questions. First and about data, it has to be a function. So you have to write it like that:
data() {
return {
styleobj: {
width: "100px",
height: "100px",
backgroundColor: "white"
}
}
}
After that, your forgot to reference your component in your Vue instance. Try that:
var vm = new Vue({
el: "#app",
components: {
brick: brick
}
})
Hope it will work.
Data must be a function like data: function(){ return obj }
Register the component using components: {yourcomponent}
You needed to use # in front of your color.
<html>
<head>
<title>v pavle</title>
<script type="text/javascript" src="https://vuejs.org/js/vue.js"></script>
</head>
<body>
<div id="app">
Hello App!
<brick>Hello Brick</brick>
</div>
<script type="text/javascript">
var brick = Vue.component("brick", {
template:
'<div :style="styl" #click="changebgcolor" #mouseover="changebgcolor" #mouseout="changebgcolor"><slot></slot></div>',
data: function(){
return {
styl: {
width: "100px",
height: "100px",
backgroundColor: "#b6d8a1",
color: "blue"
}
};
},
methods: {
changebgcolor: function() {
console.log('changebgcolor!');
this.styl.backgroundColor = "#"+ Math.floor(
Math.random() * 16777215
).toString(16);
}
}
});
var vm = new Vue({
el: "#app",
components:{brick:brick}
});
</script>
</body>
</html>
When using Vue.component, you need to make sure that you have registered all components once you start rendering your app. At the moment, you first render the main app and then register the component, so swap those around
var brick = Vue.component("brick", {
template:
'<div v-bind:style="styleobj" v-on:mouseover="changebgcolor" v-on:mouseout="changebgcolor"></div>',
data: {
styleobj: {
width: "100px",
height: "100px",
backgroundColor: "white"
}
},
methods: {
changebgcolor: function() {
this.styleobj.backgroundColor = Math.floor(
Math.random() * 16777215
).toString(16);
}
}
});
var vm = new Vue({
el: "#app"
});
The data property on your component should be a function that returns an object. Why? Because otherwise all instances of your component share the same data, meaning all tiles on your app would have the same color.
data() {
return {
styleobj: {
width: "100px",
height: "100px",
backgroundColor: "white"
}
}
},
You should first register the component before you fire up the Vue instance, just reorder your code and it works!

How to get element calling function in Vue (no event)

I have this code and I need to have the calling div on the getWidth function. Is this possible? Since it's not an event I'm not sure how this can be handled. Generally I'd do $event but it doesn't exist in this context.
<div :style="{width: getWidth($this_element)}">
</div>
This is contained in a v-for loop.
The current DOM element is already available in getWidth() via this.$el. If you wanted to access the element's width in JavaScript, you could do something like this:
<template>
<div :style="{width: getWidth()}">...</div>
</template>
<script>
export default {
methods: {
getWidth() {
return (this.$el.clientWidth * .33) + 'px';
}
}
}
</script>
new Vue({
el: '#app',
methods: {
getWidth() {
return (this.$el.clientWidth * .33) + 'px';
}
}
})
.my-el {
display: inline-block;
background: #eee;
}
<script src="https://unpkg.com/vue#2.5.17"></script>
<div id="app">
<div class="my-el" :style="{width: getWidth()}">{{getWidth()}}</div>
</div>

Vue.js css transition on class change

I am trying to use the css transition property to fade the #app background from black to white by adding a class of .lightTheme. The adding of .lightTheme class works as expected, but the transition does not.
Do I have to add the Vue transition element? And if so how? #app is not leaving/entering the dom - I just want to animate it's properties (and I don't want to add unnecessary code if possible!
HTML
<div id="app" v-bind:class="{ lightTheme: isActive }">
<button v-on:click="toggleTheme">Change theme</button>
</div>
JS
new Vue({
el: '#app',
data: {
isActive: false
},
methods: {
toggleTheme: function(){
this.isActive = !this.isActive;
// some code to filter users
}
}
});
CSS
#app {
background: black;
transition: 1s;
}
#app.lightTheme {
background: white;
}
Thanks!
To answer your question if you have to use transition for something like this, the answer is no. The solution you proposed should work out of the box.
There is probably something else that is interfering with your code, but the one you posted is correct.
I attached the working snippet.
new Vue({
el: '#app',
data: {
isActive: false
},
methods: {
toggleTheme: function(){
this.isActive = !this.isActive;
}
}
});
#app {
background: black;
transition: 1s;
}
#app.lightTheme {
background: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.min.js"></script>
<div id="app" :class="{ lightTheme: isActive }">
<button #click="toggleTheme">Change theme</button>
</div>

Pass data from child to parent in Vuejs (is it so complicated?)

I have read about it:
vuejs update parent data from child component
https://forum.vuejs.org/t/passing-data-back-to-parent/1201/2
The concept is the same, I need to pass a data object from child to parent. I have used $emit to pass data to parent component but it doesn't works. Do you know what is wrong? You can check my code here:
Vue.component('list-products', {
delimiters: ['[[', ']]'],
template: '#list-products-template',
props: ['products'],
data: function () {
return {
productSelected: {}
}
},
methods: {
showDetailModal: function (product) {
console.log('click product in child, how can i pass this product to productSelected data in parent?');
console.log(product);
this.productSelected = product;
this.$emit('clickedShowDetailModal', product);
}
}
});
var app = new Vue({
delimiters: ['[[', ']]'],
el: '#resultComponent',
data: {
listProducts: [
{'name':'test1',id:1},
{'name':'test2',id:2},
{'name':'test3',id:3}
],
productSelected: {}
},
methods: {
clickedShowDetailModal: function (value) {
console.log('value');
console.log(value);
this.productSelected = value;
}
}
});
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="resultComponent" data-toggler=".small-up-2" class="row small-up-1">
<list-products :products="listProducts"></list-products>
</div>
<script type="text/x-template" id="list-products-template">
<div>
<div class="column column-block" v-for="(product, index) in products" :product="product" :index="index" :key="product.id">
<li class="more-benefits">
<a #click="showDetailModal(product)">Click me [[ product.name ]] and check console.log ยป</a>
</li>
</div>
</div>
</script>
Props are for parent -> child
You can use $emit for child -> parent
v-on directive captures the child components events that is emitted by $emit
Child component triggers clicked event :
export default {
methods: {
onClickButton (event) {
this.$emit('clicked', 'someValue')
}
}
}
Parent component receive clicked event:
<div>
<child #clicked="onClickChild"></child>
</div>
...
export default {
methods: {
onClickChild (value) {
console.log(value) // someValue
}
}
}
You aren't listening to the event. I changed the event name to clicked-show-detail. Try this.
In the showDetailModal method of your component.
this.$emit('clicked-show-detail', product);
In your Vue.
<list-products :products="listProducts" #clicked-show-detail="clickedShowDetailModal"></list-products>
Example.
Nightmare to find "hello world" example out there for $emit so I added the example below (Minimal lines of code + semantic names of functions).
"Hello world" On click change parent data
Vue.component('child', {
template: `
<div class="child">
<button v-on:click="childMethod">CLICK - child Method pass data from product component</button>
</div>
`,
data: function () {
return {
child_msg: "message from child"
}
},
methods: {
childMethod: function() {
this.$emit('child-method', this.child_msg)
}
}
})
var app = new Vue({
el: '#app',
data: {
msg: "I am the blue parent!!!!!!!!!!!!!!!!!!",
},
methods: {
updateParent(value_from_child) {
this.msg = value_from_child;
alert("hello child" + value_from_child)
}
}
})
.child{ background: gray; padding: 15px; }
button{ cursor: pointer; }
#app{ border: 1px red dashed; padding: 15px; background: lightblue; color: blue;
}
<div id="app">
<p>{{msg}}</p>
<!-- ###### The trick happens her ###### -->
<child class="child" v-on:child-method="updateParent"></child>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.13/dist/vue.js"></script>
codepen: https://codepen.io/ezra_siton/pen/YzyXNox?editors=1010

How to get dom objects in dijit ContentPane content

There is one image in dojo/dijit's content. I want to set click event for the image but it can't catch the event.
the code in JSFiddle
<script>dojoConfig = {parseOnLoad: true}</script>
<script src='../../_static/js/dojo/dojo.js'></script>
<script>
require(["dijit/layout/ContentPane", "dojo/domReady!"], function(ContentPane){
new ContentPane({
content:"<p><img src='https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/logo_white_fe6da1ec.png' onclick='clickHandler()' /></p>",
style:"height:125px"
}, "targetID").startup();
function clickHandler()
{
alert("img clicked");
}
});
</script>
</head>
<body class="claro">
<div id="targetID">
I get replaced.
</div>
</body>
</html>
You are defining the clickHandler function within the require function. This means that it will not be available after require returns. At the console you can see an error when the image is clicked: "clickHandler is not defined". You can easily solve this by defining the clickHandler function outside of require().
<script>
require(["dijit/layout/ContentPane", "dojo/domReady!"], function(ContentPane){
new ContentPane({
content:"<p><img src='https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/logo_white_fe6da1ec.png' onclick='clickHandler()' /></p>",
style:"height:125px"
}, "targetID").startup();
});
function clickHandler()
{
alert("img clicked");
}
</script>
Edited.
require([
'dojo/dom-construct',
'dijit/layout/ContentPane',
'dojo/domReady!'
], function(domConstruct, ContentPane){
new ContentPane({
content: '<div id="imageDiv"></div>',
style: 'height:125px'
}, 'targetID').startup();
domConstruct.create('img', {
src: 'https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/logo_white_fe6da1ec.png',
onclick: function(){ alert('i have been clicked') }
}, 'imageDiv');
});
<script src="//ajax.googleapis.com/ajax/libs/dojo/1.10.4/dojo/dojo.js"></script>
<div id="targetID">I get replaced.</div>