最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - AngularJs, refresh view periodically with $interval does not update some values - Stack Overflow

programmeradmin0浏览0评论

Imagine you have a view that is populated with an http call to some service that returns a list of objects to be shown. I'd like to refresh periodically this data. I found $interval to acplish that behaviour.

The problem is that I'm not able to show a simple "isLoading" screen everytime I want to refresh the view.

I thought about something like:

   viewModel.isLoading = true;
   viewModel.refreshView();
   viewModel.isLoading = false;

Of course, isLoading is bind to some view message with angular. This isn't working, the property is always false. I found that it is because angular notifies the view of the property changed when the function called by $interval exits, but when it exits isLoading is always false.

How can I achieve to make isLoading reflects its changes even if it is inside a function?


Plunker snippet (Angular 1.6.0):

My view:

<head>
    <script src=".6.0/angular.js"></script>
    <script src="testController.js"></script>
</head>

<body>

    <div ng-app="testApp">
      <div ng-controller="testController as viewModel">

          <h1>
          "IsLoading" value: {{ viewModel.isLoading }}
          </h1>

      </div>
    </div>

</body>

My controller:

(function() {

angular.module('testApp', [])
.controller("testController", testController);

function testController($interval) {

  viewModel = this;
  viewModel.isLoading = true;

  viewModel.refreshView = function () {

      viewModel.DoSomeLongRunningOperation();

  };

  viewModel.DoSomeLongRunningOperation = function() {
      // 
  }


   var invertPropertyTimer = $interval(function () {

    viewModel.isLoading = true;
    viewModel.refreshView();
    viewModel.isLoading = false;

   }.bind(this), 1000);


}

})();

Imagine you have a view that is populated with an http call to some service that returns a list of objects to be shown. I'd like to refresh periodically this data. I found $interval to acplish that behaviour.

The problem is that I'm not able to show a simple "isLoading" screen everytime I want to refresh the view.

I thought about something like:

   viewModel.isLoading = true;
   viewModel.refreshView();
   viewModel.isLoading = false;

Of course, isLoading is bind to some view message with angular. This isn't working, the property is always false. I found that it is because angular notifies the view of the property changed when the function called by $interval exits, but when it exits isLoading is always false.

How can I achieve to make isLoading reflects its changes even if it is inside a function?


Plunker snippet (Angular 1.6.0): http://plnkr.co/edit/kIANiq6F0eM98KgHdr6i?p=preview

My view:

<head>
    <script src="https://ajax.googleapis./ajax/libs/angularjs/1.6.0/angular.js"></script>
    <script src="testController.js"></script>
</head>

<body>

    <div ng-app="testApp">
      <div ng-controller="testController as viewModel">

          <h1>
          "IsLoading" value: {{ viewModel.isLoading }}
          </h1>

      </div>
    </div>

</body>

My controller:

(function() {

angular.module('testApp', [])
.controller("testController", testController);

function testController($interval) {

  viewModel = this;
  viewModel.isLoading = true;

  viewModel.refreshView = function () {

      viewModel.DoSomeLongRunningOperation();

  };

  viewModel.DoSomeLongRunningOperation = function() {
      // 
  }


   var invertPropertyTimer = $interval(function () {

    viewModel.isLoading = true;
    viewModel.refreshView();
    viewModel.isLoading = false;

   }.bind(this), 1000);


}

})();
Share Improve this question asked Jan 8, 2017 at 21:52 Francesco BonizziFrancesco Bonizzi 5,3026 gold badges54 silver badges89 bronze badges 7
  • 1 call viewModel.isLoading = false; when loading actually finished. e.g. inside some promise then handler – Bryan Chen Commented Jan 8, 2017 at 22:09
  • @BryanChen Can you please provide an example? – Francesco Bonizzi Commented Jan 9, 2017 at 8:03
  • refreshView takes no time to run, so the loading time is none. – Bryan Chen Commented Jan 9, 2017 at 8:25
  • This example is very simple, in my real case I have a real long running operation inside DoSomeLongRunningOperation() and it doesn't work either. – Francesco Bonizzi Commented Jan 9, 2017 at 8:31
  • 1 In order to acplish this, either refreshView or DoSomeLongRunningOperation would need to be a promise. – Claies Commented Jan 10, 2017 at 22:54
 |  Show 2 more ments

5 Answers 5

Reset to default 3 +50

I'm not sure about this one, but what about using promises?

You can return a promise object in your refreshView function. This will help you to see if the task is done in your DoSomeLongRunningOperation function later on in the interval.

  viewModel.refreshView = function () {
  return new Promise(function(resolve, reject){
          viewModel.DoSomeLongRunningOperation();
          resolve();
      });
  };

Do some tasks in your operation function. Lets say there is an operation that takes 200ms. (I used setTimeout but angular has its own testable $timeout service also.)

  viewModel.DoSomeLongRunningOperation = function() {
      //do your thing.  

      setTimeout(function(){
            console.log("timeoutends.")
      }, 200);
  }

In your interfal, listen the promise with then() and change isLoading variable. That promise resolver will fire after DoSomeLongRunningOperation func is done;

var invertPropertyTimer = $interval(function () {

  viewModel.isLoading = true;
    viewModel.refreshView().then(() =>{
      viewModel.isLoading = false;
    });

}.bind(this), 1000);

I haven't check the if it works or the syntax is right. Also you might need to use $apply() if the data is changing but the view is not.


Links:

  • https://davidwalsh.name/promises
  • http://jimhoskins./2012/12/17/angularjs-and-apply.html

DoSomeLongRunningOperation make http call? so it should return a Promise and in the then you should change the isLoading to false after the http call respond.

other thing that i think you didn't thought about- what if the http call will take more time than the next interval time (then you will have two http calls at the same time)? i think you should call the refreshView with $timeout after the previous refreshView done.

Here is a working example.

Use a Promise, and set isLoading = false in the promise's then() method! See it working HERE.

(function() {

  angular.module('testApp', [])
    .controller("testController", testController);

  function testController($interval, $timeout) {

    viewModel = this;
    viewModel.isLoading = false;

    viewModel.someData = "Haven't loaded anything yet...";

    viewModel.refreshView = function() {

      viewModel.isLoading = true;

      viewModel.DoSomeLongRunningOperation();

    };

    viewModel.DoSomeLongRunningOperation = function() {
      //Create a Promise object.
      //This will likely be an $http.get() call.
      var longRunningOperation = $timeout(function() {
        return 'Some data retrieved at ' + new Date();
      }, 500);

      //This runs once the Promise has resolved
      longRunningOperation.then(function(data) {

        //Do something with the retrieved data
        viewModel.someData = data;

        //Now we're done loading!
        viewModel.isLoading = false;
      });
    }


    var invertPropertyTimer = $interval(viewModel.refreshView.bind(this), 3000);


  }

})();

In your plunker it is not working because angular is refreshing the DOM after the $interval function finishes, and the value of isLoading is still false.

As said in the ments the idea is to set the flag loading to false on the resolution of a promise.

If your refresh is made through HTTP and you are using $http service then that would be really easy as:

$http.get().then(function(){
    viewModel.isLoading = false;
});

For the demo you wanted in your long operation I had to fake a asynchronous operation, I used $q:

  viewModel.DoSomeLongRunningOperation = function() { 
      var defered = $q.defer();
      setTimeout(function(){ defered.resolve(); }, 300);
      return defered.promise;
  }

finally the interval part:

var invertPropertyTimer = $interval(function () {
    viewModel.isLoading = true;
    viewModel.DoSomeLongRunningOperation().then(function(){viewModel.isLoading = false;});
}.bind(this), 1000);

Here is a link to the plunker that "blink" as you want it: http://plnkr.co/edit/xsivUm7yVNeLEivQUoLM?p=preview

Try this

viewModel.isLoading = true;
$timeout(function(){
  viewModel.refreshView();
  viewModel.isLoading = false;
}, 0);

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论