I can't figure out why this code works..
data: {
return {
userMinerals: 0,
mineralsLimit: 1000,
miners: 0,
superMiner: 0,
minerPrice: 10,
superMinerPrice: 100,
minersLimit: 10
}
}
methods: {
counter() {
setInterval(() => {
this.userMinerals += this.miners;
if(this.checkLimit(this.userMinerals, this.mineralsLimit)) {
this.userMinerals = this.mineralsLimit;
}
}, 100);
},
addMiner() {
if (this.userMinerals >= this.minerPrice) {
this.miners += 1;
this.userMinerals -= this.minerPrice;
this.counter();
}
}
}
.. but if I try to put parameters into counter() the code stops working
methods: {
counter(typeOfCredits) {
setInterval(() => {
typeOfCredits += this.miners;
if(this.checkLimit(this.userMinerals, this.mineralsLimit)) {
typeOfCredits = this.mineralsLimit;
}
}, 100);
},
addMiner() {
if (this.userMinerals >= this.minerPrice) {
this.miners += 1;
this.userMinerals -= this.minerPrice;
this.counter(this.userMinerals);
}
}
}
From the console I can see that typeOfCredits gets incremented as it should but it doesn't update the value in the view.
Thx for help
You cant reference a parameter and expect it to be changed outside, but you can pass a reference to an object that can change something outside.
var $this = this;
this.counter({
get() { return $this.userMinerals },
set(val) { $this.userMinerals = val }
});
and then use in the counter like this
counter(typeOfCredits) {
setInterval(() => {
typeOfCredits.set(typeOfCredits.get() + this.miners);
if(this.checkLimit(this.userMinerals, this.mineralsLimit)) {
typeOfCredits.set(this.mineralsLimit);
}
}, 100);
},
jsfiddle
typeOfCredits is a parameter to the function. Parameters are passed by value. Modifying it is like modifying a local variable.
Related
I have an object with two arrays (_licky, _unlucky). and methods that randomly push names into one of the two arrays. But my code does not work for some reason... What is wrong with my code?
const luckGame = {
_lucky: [],
_unlucky: [],
pushGamer(name) {
return name;
},
getRandomNumber (random) {
return random = Math.floor(Math.random() * 2);
},
pushGamerIntoArray () {
return {
if (this.getRandomNumber() === 0 ) {
this._lucky.push(this.pushGamer());
} else {
this._unlucky.push(this.pushGamer());
}
};
},
};
this.pushGamer('John');
this.pushGamer('Nick');
this.pushGamer('Maria');
this.pushGamer('Sarah');
this.pushGamer('Ron');
this.pushGamer('Lisa');
console.log(luckGame._lucky);
console.log(luckGame._unlucky);
I fixed it. Now it works. thank you all!!
const luckGame = {
_lucky: [],
_unlucky: [],
getRandomNumber (random) {
return random = Math.floor(Math.random() * 2);
},
pushGamerIntoArray (name) {
if (this.getRandomNumber() === 0 ) {
return this._lucky.push(name);
} else {
return this._unlucky.push(name);
}
},
};
luckGame.pushGamerIntoArray('John');
console.log(luckGame._lucky);
console.log(luckGame._unlucky);
There were a few small errors on your part. I have rewritten it in a class variant. It works, but I would still improve a few things. You can test it a bit.
const luckyGame = class {
constructor() {
this._lucky = [],
this._unlucky = []
}
pushGamer(name) {
this.pushGamerIntoArray(name)
}
getRandomNumber (random)
{
return random = Math.floor(Math.random() * 2);
}
pushGamerIntoArray (newGamerName)
{
if (this.getRandomNumber() === 0 ) {
this._lucky.push(newGamerName)
} else {
this._unlucky.push(newGamerName)
}
}
getLuckies () {
return this._lucky
}
getUnluckies () {
return this._unlucky
}
}
let luckygame = new luckyGame()
luckygame.pushGamer('John');
luckygame.pushGamer('Nick');
luckygame.pushGamer('Maria');
luckygame.pushGamer('Sarah');
luckygame.pushGamer('Ron');
luckygame.pushGamer('Lisa');
luckygame.pushGamer('John');
console.log(luckygame.getLuckies());
console.log(luckygame.getUnluckies());
I'm confused how to add a set Interval variable in my vue file myTimer = setInterval(function, time).
The goal of my program is to change the number variable every 3 seconds, to access the object that I created to let it display different background images but I also have 2 buttons that can navigate through the background as well.
This is my template code
<div class="background" :style="{backgroundImage: `url(${Images[`${number}`].picture})`}">
<div class="content_container">
<div v-on:click="prev" class="prevButton"><p><</p></div>
<div class="title_container">
<div class="theTitle"><p>{{Images[`${number}`].Title}}</p></div>
</div>
<div v-on:click="next" class="nextButton"><p>></p></div>
</div>
</div>
This is my set Interval code
export default {
data: () => ({
number: 0,
myTimer
}),
mounted:function() {
this.$nextTick(() => {
this.timer();
});
},
methods:{
timer: function() {
var myTimer = setInterval(this.counter, 3000);
},
counter:function(){
if(this.number >= (this.Images.length -1)){
this.number = 0;
}
else{
this.number +=1;
}
},
next()
{
clearInterval(this.myTimer)
if(this.number >= (this.Images.length -1)){
this.number = 0;
}
else{
this.number +=1;
}
},
I want to clearInterval at the next() function how can I access the variable from another function.
your myTimer need be in this context.
export default {
data: () => ({
number: 0,
myTimer: 0, // init myTimer
}),
mounted: function () {
this.$nextTick(() => {
this.timer();
});
},
methods: {
timer: function () {
this.myTimer = setInterval(this.counter, 3000); // in this.myTimer
},
counter: function () {
if (this.number >= this.Images.length - 1) {
this.number = 0;
} else {
this.number += 1;
}
},
next() {
clearInterval(this.myTimer); // clear
if (this.number >= this.Images.length - 1) {
this.number = 0;
} else {
this.number += 1;
}
this.timer(); // start next
},
},
};
i'm working on small project using Vue.js i have created a pagination system to display my database users in a table, i have a small issue, i would like to know how can i stop the setinterval if my getResult function page variable is bigger than 1.
this is my code :
data(){
return {
editMode : true,
customer_id : null,
laravelData : {},
formFields : {}
}
},
methods:{
getResults(page = 1){
axios.get('Thirdparty/loadCustomers/' + page).then(response => {
this.laravelData = response.data;
});
}
},
created(){
self = this;
setInterval(function(){
self.getResults();
}, 5000);
}
First and foremost, always capture identifiers from setInterval and setTimeout.
By capturing your interval ID you can later remove it from within your callback when the page value is larger than its default (1).
EDIT: The OP would like to be able to reset the interval when page resets.
created() {
this.resetInterval();
},
methods: {
resetInterval() {
this.currentInterval && clearInterval(this.currentInterval);
this.currentInterval = setInterval(() => this.getResults(), 5000);
},
getResults(page = 1) {
if (page == 1 && !this.currentInterval) {
this.resetInterval();
} else {
clearInterval(this.currentInterval);
}
axios.get('Thirdparty/loadCustomers/' + page).then(response => {
this.laravelData = response.data;
});
}
}
data(){
return {
editMode : true,
customer_id : null,
laravelData : {},
formFields : {},
currentInterval : null
}
},
methods:{
getResults(page = 1){
clearInterval(this.currentInterval);
axios.get('Thirdparty/loadCustomers/' + page).then(response => {
this.laravelData = response.data;
});
},
created(){
self = this;
self.currentInterval = setInterval(function(){
self.getResults();
}, 5000);
}
Just wanted to share a little trick I learned to pass variables into the scope of your JS Array.forEach() method.
I had a situation where I needed to use a forEach loop to build a dataset. But I needed to access variables in the current scope as well (I needed to be able to reference this in the loop).
This is the situation I was in:
var dataset = {
data: [],
backgroundColor:[],
};
items.forEach(function (item) {
dataset.data.push(item.age);
if (item.age < 2) {
dataset.bgColor.push(this.green);
} else if (item.age < 5) {
dataset.bgColor.push(this.yellow);
} else {
dataset.bgColor.push(this.red);
}
}, this);
this.refreshGraph(dataset);
Dataset isn't accessible from within the loop. So how do we access it while iterating?
I haven't seen this solution on stack overflow and it didn't fit any question I could find.
Answer below:
With the abilities of es6
If you'll use an Arrow Function the this will be taken from
items.forEach(item => {
// You can use this as out of the forEach scope
});
From MDN Web Docs:
An arrow function does not have its own this. The this value of the
enclosing lexical scope is used; arrow functions follow the normal
variable lookup rules. So while searching for this which is not
present in current scope, an arrow function ends up finding the this
from its enclosing scope.
Another nice explanation:
https://hackernoon.com/javascript-es6-arrow-functions-and-lexical-this-f2a3e2a5e8c4
If you have a function out of scope of some data yet need to access it, you can use a curried function that takes that dataset as the first parameter and can still use this normally throughout:
//curried function that uses `dataset` and `this` but it is not
//in the context where the iteration happens
function makeLoopCallback(dataset) {
return function(item) {
dataset.data.push(item.age);
if (item.age < 2) {
dataset.bgColor.push(this.green);
} else if (item.age < 5) {
dataset.bgColor.push(this.yellow);
} else {
dataset.bgColor.push(this.red);
}
}
}
//object to serve as `this` context for a function
var obj = {
green: "Green",
yellow: "Yellow",
red: "Red",
doSomething: function(items) {
var data = {
data: [],
bgColor:[],
};
items.forEach(makeLoopCallback(data), this);
return data;
}
}
//set up some dummy data
var input = [ { age: 1 }, { age: 2 }, { age: 3 }, { age: 4 }, { age: 5 }, { age: 6 } ];
//call the function
console.log(obj.doSomething(input))
An alternative is to use Array#reduce instead of Array#forEach with a function that takes two parameters directly. Since .reduce cannot set the this context, you can just use Function#bind to do it:
//external function that uses `dataset` and `this` but it is not
//in the context where the iteration happens
function external(dataset, item) {
dataset.data.push(item.age);
if (item.age < 2) {
dataset.bgColor.push(this.green);
} else if (item.age < 5) {
dataset.bgColor.push(this.yellow);
} else {
dataset.bgColor.push(this.red);
}
return dataset;
}
//object to serve as `this` context for a function
var obj = {
green: "Green",
yellow: "Yellow",
red: "Red",
doSomething: function(items) {
var data = {
data: [],
bgColor:[],
};
return items.reduce(external.bind(this), data);
}
}
//set up some dummy data
var input = [ { age: 1 }, { age: 2 }, { age: 3 }, { age: 4 }, { age: 5 }, { age: 6 } ];
//call the function
console.log(obj.doSomething(input))
The solution is to pass a JSON object as the this argument.
so before we had:
Array.forEach(function(){}, this)
// "this" is just an object ^^^^ just like anything else in JavaScript
Now we have:
Array.forEach(function(){}, {_self: this, dataset: dataset})
// you can access _self and dataset just as if they were in scope
And now you can make data changes while iterating with an anonymous function :)
Full example:
var dataset = {
data: [],
backgroundColor:[],
};
items.forEach(function (item) {
dataset.data.push(item.age);
if (item.age < 2) {
dataset.bgColor.push(_self.green);
} else if (item.age < 5) {
dataset.bgColor.push(_self.yellow);
} else {
dataset.bgColor.push(_self.red);
}
}, { _self: this , dataset: dataset});
Array.prototype.forEach(callbackFun, ?this)
You can pass dataset as this argument to forEach
var dataset = {
data: [],
backgroundColor:[],
};
items.forEach(function (item) {
this.dataset.data.push(item.age);
if (item.age < 2) {
this.dataset.bgColor.push(this.tempThis.green);
} else if (item.age < 5) {
this.dataset.bgColor.push(this.tempThis.yellow);
} else {
this.dataset.bgColor.push(this.tempThis.red);
}
}, {tempThis:this,dataset:dataset});
this.refreshGraph(dataset);
I am using vue.js in this case but I guess it would apply in plain JS too. The problem is that when I am in a function that is in another function I am having to call variables by their full path like - Object.variable instead of this.variable. Is there a way to use this.timer, this.pages instead of TVComponent.pages etc.
const TVComponent = new Vue ({
el: '.tvContent',
data:
{
current_page: 0,
timer: 0,
pages: [
{ page: '/', interval: 10 },
{ page: 'tv/calls', interval: 10 },
{ page: 'tv/general', interval: 10 }
]
},
methods:
{
tvTimer()
{
setInterval(function() {
TVComponent.timer++;
if (TVComponent.timer == TVComponent.pages[TVComponent.current_page].interval) {
console.log('it is time!!');
}
console.log(TVComponent.pages[TVComponent.current_page].page);
}, 1000);
},
})
Classic problem of this being not what you expect in a function
A. bind it
methods:
{
tvTimer()
{
setInterval(function() {
// ...
}.bind(this), 1000);
},
})
B. use a closure
methods:
{
tvTimer()
const _this = this
{
setInterval(function() {
_this.timer ...
}, 1000);
},
})
C. use an arrow function
methods:
{
tvTimer()
{
setInterval(() => {
this.timer ...
}, 1000);
},
})
This is one of those things one has to really understand about JS in order to not fall for it over and over in different places. I suggest this ebook:
https://github.com/getify/You-Dont-Know-JS/blob/master/up%20%26%20going/ch2.md#this-identifier
The function you pass to setInterval receives its own context, however if you use an arrow function it will use the current context instead, and ignore the one given by setInterval.
setInterval(() => { ... }, 1000)
This is because setInterval() this object is not same as vue.js this, since they have different scopes.
Try to assign this object to new variable before entering the problematic function's scope.
let self = this;
tvTimer()
{
setInterval(function() {
self.timer++;
if (self.timer == self.pages[self.current_page].interval) {
console.log('it is time!!');
}
console.log(self.pages[self.current_page].page);
}, 1000);
},
See: https://developer.mozilla.org/en-US/docs/Glossary/Scope
I think you have to bind it in this context. We do it this way in React classess.