Well, I have some function that invokes via ng-click.
For example, this function set variable a to true, and after few seconds variable should become false.
Function looks like this one:
$scope.do = function (){
$scope.a = true;
$timeout($scope.a=false,5000);
}
But when I call this function, varibale a become false immediately, without wating 5 secs.
Am I do everything right ?
Demo on Plnkr.
How can I achieve "waiting" after click with AngularJS ?
$timeout (as well as native javascript setTimeout) expects a function as the first argument:
$scope.do = function() {
$scope.a = true;
$timeout(function() {
$scope.a = false;
}, 5000);
};
This function reference you provide, will then be invoked after timer is up.
Demo: http://plnkr.co/edit/Rg82R98gViWJ8YZtJD8z?p=preview
This should do
$scope.do = function (){
$scope.a = true;
$timeout(function(){$scope.a=false},5000);
}
Related
I used AngularJS factory to create new instance model for my project, and each model contain a progress value that will be incremented, paused, and set back to 0 based on "start", "pause", and "stop" user actions.
app.factory('ModelA', ['$timeout', function($timeout) {
function ModelA(progress) {
this.progress = progress;
};
ModelA.prototype = {
startProgress: function() {
this.counterFunction();
},
counterFunction: function() {
this.progress++;
if(this.progress == 100) {
this.progress = 0;
}
//console.log(this.progress);
//console.log(this.counterFunction);
progressTimeout = $timeout(this.counterFunction, 1000);
},
// Haven't tested the method below
pauseProgress: function() {
$timeout.cancel(progressTimeout);
},
stopProgress: function() {
$timeout.cancel(progressTimeout);
this.progress = 0;
}
};
return ModelA;
}]);
For some reason, when I call startProgress() in the ng-click expression function, the progress will increment 1 and then stop. I added logs to check this.counterFunction for every call. I realized it only prints out 1 and the whole counterFunction on the first time. As for the second time, this.progress will be NaN and the counterFunction will show undefined.
I'm new to AngularJS, could someone please help me out? Thanks.
The this object in the function called by the $timeout, i.e. this.counterFunciton is not the ModelA instance, therefore you should use
$timeout(this.counterFunction.bind(this), 1000) instead.
You can read up this article about binding this object in JavaScript.
A working codepen for your reference.
The execution context this changes when the $timeout gets executed. You would need to preserve the ModelA this in $timeout(this.counterFunction.bind(this), 1000). You bind and pass the this to this.counterFunction and thus counterFunction has the right access to this.progress.
Check here more info about the this problem here. $timeout is the wrapper for window.setTimeout
https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout#The_this_problem
I have a controller and a factory. A function(myfunc) inside the factory(searchFactory) is called by ng-click of a button. after which I call a function(waitfunction) which is outside the conntroller. In that function timeout of 2 sec is used and then a value is changed and returned to the controller. How can I make sure that the value is updated in the controller after 2 sec. JSfiddle : http://jsfiddle.net/zohairzohair4/cRr9K/1334/
var search_name
var angularjsapp = angular.module('graphApp', ['ngAnimate', 'ui.bootstrap']);
angularjsapp.factory('searchFactory', function() {
//return $resource('friends.json');
return{
myfunc:function(search_name){
console.log('ok')
keyword_type = 1
loading_value = waitfunction(search_name)
console.log(loading_value)
return loading_value
}
}
});
angularjsapp.controller('AccordionDemoCtrl', function($scope,searchFactory) {
$scope.count = 0;
$scope.namesPerPage = 10
$scope.currentPage = 1;
$scope.searchFactory = searchFactory.myfunc
});
function waitfunction(search_name){
value = 0
window.setTimeout(function () {
value = 1;
}, 2000);
return value
};
Using the setTimeout function will bypass angular's dirty checking. If you want to use async functionality outside of angulars dirty-checking, you need to trigger angular to do the dirty checking again. You can do that by calling $scope.$apply(); or wrap your async call with a function like this:
$scope.$apply(function() {
...
});
For your specific need - angular already have a number of async methods to replace the default javascript ones and i'd suggest you use that instead of javascripts timeout:
$timeout(function () {
value = 1;
}, 2000);
You can read more about $timeout here: https://docs.angularjs.org/api/ng/service/$timeout
Your waitfunction doesn't seem to be doing what you want it to do. It will return the value long before the timeout changes it. This happens because you're just referencing a simple value. If you pass an object and modify that objects property, then you can achieve what you're trying to do:
function waitfunction(search_name){
var obj = { value: 0 };
$timeout(function () {
obj.value = 1;
}, 2000);
return obj;
};
You then need to bind to the returning objects .value property instead.
I see your plunker and there is a lot of work to be done. It doesn't seem like you're binding the resulting object to anything. I think this post helps to solve atleast the async problem associated with calling setTimeout and the problem of binding to simple values.
You need to use $timeout
$timeout(function() {
value=1;
}, 2000);
I want to have a custom $watch. This watch I will make on a variable. I want when a the value of a variable is changed to see a message, and when the variable is not change his value to see another message.
An example is the next one: I have a textbox where I watch when a user writes something. I want to see a message when he writes something and when stop to see another message.
You can check with a timeout:
$scope.$watch("myVar",
function handleMyVarChange( newValue, oldValue ) {
//Change Display Message to Editing Message
//Timeout to checkif stop writing
$timeout(function (newValue) {
if(newValue === myVar) {
//Change Display Message to Not Editing Message
}
}, 100);
}
);
The concept I would recommend is called debounce in many javascript libraries. You give a function to call and a length of time that must pass without the function being called again before a callback is fired. Often times you can supply the option of firing the callback on the initial call as well. This would allow you to flip a switch on the first call, then flip it back once the amount of time has passed without the function being called again.
See: http://davidwalsh.name/javascript-debounce-function
Ex:
(a better example would create a debounce service using $timeout eliminating the need for $scope.$evalAsync())
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
var toggle = debounce(function() {
$scope.typing = !$scope.typing;
$scope.$evalAsync();
}, 500, true)
$scope.$watch('name', toggle)
});
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this;
var args = arguments;
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow){
func.apply(context, args);
}
function later() {
timeout = null;
func.apply(context, args);
}
};
}
Recently I found this http://jsfiddle.net/jaredwilli/SfJ8c/ and I am able to get it works.
var app = angular.module('miniapp', []);
app.directive('resize', function ($window) {
return function (scope, element) {
var w = angular.element($window);
scope.getWindowDimensions = function () {
return {
'h': w.height(),
'w': w.width()
};
};
scope.$watch(scope.getWindowDimensions, function (newValue, oldValue) {
scope.windowHeight = newValue.h;
scope.windowWidth = newValue.w;
}, true);
w.bind('resize', function () {
scope.$apply();
});
}
})
But the problem is, I don't know how it works? Why scope.$apply()? What is the purpose of it? Why scope.getWindowDimensions will get updated when window is resized?
The first argument to $watch can be a string or a function. If you pass a string like $scope.$watch('foo', it is watching $scope.foo. If you pass a function, then the watch is on the return value of the function. Angular will fire the function on every $digest cycle. If the returned value is different than the previous $digest cycle, the callback function (second parameter) will fire. In this code, scope.getWindowDimensions is a function being passed and when its return value is different, the callback will fire. So, every $digest cycle, if w.height() or w.width() have changed, the callback is fired, and the $scope properties are updated. Finally, you have the true (third) parameter set, which makes the $watch a deep watch, so that Angular will thoroughly check the object, to tell whether it is identical, even though it's a new object every time. Without this, Angular will do a quick check, see that it's a new object, and start an infinite loop.
scope.$watch(scope.getWindowDimensions, function (newValue, oldValue) {
scope.windowHeight = newValue.h;
scope.windowWidth = newValue.w;
}, true);
Lastly, the code above won't do anything unless a $digest cycle is triggered. The following code attaches an event listener to window so that the function is fired when the window is resized. scope.$apply() just triggers a $digest cycle so that scope.getWindowDimensions will be checked, and the callback will be fired.
w.bind('resize', function () {
scope.$apply();
});
With all that said, I find this code to be a bit awkward. This is how I'd write it. This way makes a lot more sense to me - easier to read, and should be more performant.
app.directive('resize', function ($window) {
return function (scope, element) {
var w = angular.element($window);
w.bind('resize', function () {
// trigger $digest when window is resized and call `update` function
scope.$apply(update);
});
update(); // initial setup
function update() {
var height = w.height();
var width = w.width();
scope.windowHeight = height;
scope.windowWidth = width;
scope.style = function() {
return {
'height': (height - 100) + 'px',
'width': (width - 100) + 'px'
};
};
}
}
})
Live demo here.
First of all its better to google first and then ask your doubt here but anyways if you didn't get anything from it here is the summary:-
w.bind('resize',callback)
function binds the resize event of window to w therefore if window resize value of w.height() and w.width() change So you are having scope.$watch(scope.getWindowDimensions,callback); watch the changes in scope.getWindowDimensions which is definately gonna change on changing the window size which in turn calll the callback function.
Now why we are using scope.$apply();The reson is simple w.bind is out of scope of angular so you need to run the digest cycle which is going to run manually by scop.$apply.
Sorry for bad english :-P
I have this function.
function changeFrame(){
var time = setInterval(start, 250);
}
and I want to stop it from firing in another function, but haven't been able to figure out how to do it.
Do you mean this?
function changeFrame(){
var time = setInterval(function() {
// Do stuff
}, 250);
}
Think it's in the comments.
Ok amended the fiddle to do what you want. I made time a global var. Call clearInterval in stop with the global var http://jsfiddle.net/QNWF4/3/
In order to call clearInterval you need to have the handle returned by setInterval. That means something will either be global to the page or global to a containing function in which your script resides.
function Timer()
{
var handle = null;
this.start = function (fn,interval) {
handle = setInterval(fn,interval);
};
this.stop = function ()
{
if (handle) { clearInterval(handle); handle = null; }
};
return this;
}