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);
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
My problem is pretty simple and I think lot of programmers already must have faced this.
I am using angularjs and my javascript looks as follow:
controller('myController', function())
{
if(validateForm())
{
//do after-validation stuff
}
validateForm = function()
{
var val = undefined;
$http.get('api/validate/').
success(function(data){
val = true;
}).
error(function(data){
val =false;
});
while(!val)
{ // loop over till val has value
}
return val;
}
}
I have two questions:
1) when I make http call it returns a promise object and it resolves to either success or error function. If that is the case then the while loop in this code should not be infinite, but it is. By debugging I found that if I assign a value to variable 'var' then only the http call is made i.e when the loops ends.
why http call, being a deferred call, waits on the while loop to finish?
2) If I remove the while loop, no matter what the value of 'var' is, the function returns. How can I make it return only if 'var' is defined?
JavaScript is single threaded, so the http success/failure never get executed because the while loop is going. This is causing your infinite loop.
Return the promise and use that in the calling function.
Well usually ajax calls like these are not synchronous, having said that, when your while loop is hit your variable val is not yet set. What you should do is, just call a function in either success or error case and that function should have your while loop. Or try to make async ajax call which I am not sure if possible with selected method. So your new code will be something like
function mycallback(someval){
while(!someval)
{ // loop over till val has value
}
}
controller('myController', function())
{
if(validateForm())
{
//do after-validation stuff
}
function()
{
var val = undefined;
$http.get('api/validate/').
success(function(data){
val = true;
mycallback(val);
}).
error(function(data){
val =false;
mycallback(val);
});
}
}
depending on your need you need to change flow to meet this sync/async call. I don't recommand syncing the call as it slow down the execution for time your get ajax response. You should practice to use Async call only.
Refactor, and use the .then
angular.module("myModule")
.controller("myController", ["$scope", "$http", function ($scope, $http) {
var controller = this;
angular.extend(controller, {
// ...
submit : submit,
handleSuccess : function () { /* ... success */ },
handleError : function (err) { /* error */ }
});
function submit (form) {
validateForm(form)
.then(controller.handleSuccess, controller.handleError)
.then(controller.cleanUp);
}
function validateForm (form) {
return $http.get("...").then(function (response) { return response.data; });
}
}]);
It should all be readable, now.
Additionally, the logic doesn't go inside of "success", instead it goes inside of subsequent .then calls, so you can keep your submit clean and readable.
But you can not ever do something like
while (true) {
}
or
for (var i = 0; i < 10000000; i += 1) { }
All you are doing is locking your browser, preventing people from using the page.
You can't animate CSS, you can't read AJAX values, you probably can't even close the tab (depending on the browser).
Everything should either be reactive (event-based) or asynchronous, using callbacks or promises.
I'm trying to change an input field's value when a video loads. Here's the relevant code:
This works:
$scope.stopRecording = function () {
// value of the input changes as expected
$scope.videoEndPos = 10;
};
This doesn't
$scope.stopRecording = function () {
video.onloadedmetadata = function() {
$scope.videoEndPos = 10;
// the value of the input does not change, but this still correctly outputs 10
console.log($scope.videoEndPos);
};
};
In an effort to keep this short I left out some crucial video stuff there, but that part is working and the onloadedmetadata is firing properly, so its something funky with angular and the input. However, let me know if you suspect I've left out some relevant code.
The video.stopRecording happens outside of the Angular universe, so it does not know about the change. What you need to use is $scope.$apply, which allows you to execute changes to the scope from outside of Angular.
$scope.stopRecording = function () {
video.onloadedmetadata = function() {
$scope.$apply(function(){
$scope.videoEndPos = 10;
});
// the value of the input does not change, but this still correctly outputs 10
console.log($scope.videoEndPos);
};
};
The video.onloadedmetadata is probably an asynchronous call that doesn't return until after the digest loop. Change it to this:
$scope.stopRecording = function () {
video.onloadedmetadata = function() {
$scope.$apply(function() {
$scope.videoEndPos = 10;
});
};
};
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);
}
I have these functions that do calculations of values from input fields using ng-model.
$scope.EmployeeCompetenceObs = function () {
return $scope.user.percentEmployeesCompetentAfterTraining - $scope.user.percentEmployeesCompetentBeforeTraining;
}
and what I am trying to do is get the value from that function and use it in another function:
$scope.EmployeeCompetenceAtt = function () {
return EmployeeCompetenceObs() - $scope.user.adjustmentFactorsEmployeeCompetence;
}
but when I call {{EmployeeCompetenceAtt()}} it comes up ad undefined...what am i doing wrong?
The EmployeeCompetenceObs is a function in the $scope, as you declared it, so you should invoke it by doing:
$scope.EmployeeCompetenceObs()
You omitted that part in your invocation and that is most likely the cause of the error.