0

I tried to send data from JavaScript to html with Angular... I have this code

phonecatControllers.controller('start', ['$scope', function($scope){
    $scope.lloadd=true; 
    console.log('data - '+$scope.lloadd); 

    function second_passed() {
        $scope.lloadd=false; 
        console.log('data - '+$scope.lloadd); 
        alert('sdasd');
    }
    setTimeout(second_passed, 3000); 
}]);

And this HTML code

<div  ng-show="lloadd">
12312312
</div>

When I start app DIV is visible...but after 3 sec i see only alert... This is my log

01-08 16:34:25.608: I/chromium(22042): [INFO:CONSOLE(179)] "data - true", source: file:///android_asset/www/js/controllers.js (179)

01-08 16:34:28.613: I/chromium(22042): [INFO:CONSOLE(182)] "data - false", source: file:///android_asset/www/js/controllers.js (182)
Henrik Andersson
  • 45,354
  • 16
  • 98
  • 92
Maksim Morozov
  • 191
  • 3
  • 11

2 Answers2

3

You must use angularjs $timeout instead of setTimeout. With setTimeout angular doesn't get notified that something changed, but with $timeout it does.

But you can also use $scope.$apply() in your second_passed(), but I do not recommend that.

ewooycom
  • 2,651
  • 5
  • 30
  • 52
  • Isn't the that a scope object is getting updated by the timeout function already telling angular something has changed? How is using angular timeout adding more value? – Shouvik Jan 08 '15 at 10:51
  • @Shouvik http://stackoverflow.com/questions/23070822/angular-scope-apply-vs-timeout-as-a-safe-apply This is probably answer to your question, even tho that question is not exactly the same, you will probably see what is going on. – ewooycom Jan 08 '15 at 10:53
  • @Shouvik remember angular is a javascript framework, and not magic. Changing a field on the `$scope` does not automagically trigger anything. In order to know something has changed, code has to run to check it against its previous value. That code is called the "digest cycle", which `$timeout` triggers. If the digest didn't have to be manually triggered, that would imply it was running on a never-ending loop, which would use a huge amount of resources. – Ed_ Jan 08 '15 at 10:58
  • @EdHinchliffe agreed that it will be heavy to watch the entire scope all the time, but I have not experienced issue with updated to a scope variable not getting reflected on the UI. I presumed it's because the scope object items are directly bound to UI by reference. Ergo, updated to scope items are automatically triggering a rebind on the UI. What you are implying is the dom need to explicitly rebind and function like $timeout have the effect of calling something like a broadcast when a scope value is update. – Shouvik Jan 08 '15 at 11:05
  • Okay, I get it now. `$scope.$apply()` is used to refresh the dom in this particular scenario. Thanks @Meznaric that clears it up! :) – Shouvik Jan 08 '15 at 11:08
  • 1
    @Shouvik there is no such thing as binding to the UI by reference - javascript and HTML aren't linked like that. The reason you haven't had to use `$scope.$apply()` or similar in your experience is probably because you're always making changes to the `$scope` that are called in places where angular's native code triggers a `$digest` automatically (like in a controller, as a result of `$timeout`, or `$http`, etc. – Ed_ Jan 08 '15 at 11:36
1

The problem is you're using setTimeout to cause a change to angular data on the $scope that angular doesn't know about.

You need to read about angular's $digest cycle, but in short, you need to tell angular that some event has happened.

For timeouts, angular provides a service that does this for you, appropriately named $timeout, so it's very simple:

phonecatControllers.controller('start', function($scope, $timeout){
 $scope.lloadd=true; 
 console.log('data - '+$scope.lloadd); 
 function second_passed() {
    $scope.lloadd=false; 
    console.log('data - '+$scope.lloadd); 
    alert('sdasd');
 }
 $timeout(second_passed, 3000); 
});

Of course be sure to add $timeout to the arguments to your controller function to ensure it gets injected.

Note that you can achieve the same result using $scope.$apply() to trigger a digest inside your second_passed function:

function second_passed() {
  $scope.$apply( function () {
    $scope.lloadd=false; 
  });
  console.log('data - '+$scope.lloadd); 
  alert('sdasd');
}

But this is not advisable for a couple of reasons:

  1. It's more code!
  2. Manually triggering digests using $scope.$apply() can cause problems if there is another digest in progress already, so it's generally best to avoid it.
  3. $timeout adds more than just a $scope.$apply wrapper - it also returns a promise, and has a handy flush() decorator when you're testing.

Whether you are in the habit of using promises and writing tests or not, you should use $timeout, because someday you will want to use those features.

Ed_
  • 18,798
  • 8
  • 45
  • 71