0

I found an compatibility issue using the two mentioned components on same list, my html and js code below: HTML:

    <ion-content ng-controller="homeCtrl">
    <ion-refresher on-refresh="loadNewContent()" pulling-text="LoadMore..." spinner="android"></ion-refresher>
    <div ng-repeat="item in items">

        <a href="#/detail" class="thumb"><img ng-src="{{pathUrl+item['path']}}" style="height: auto;width:100%;"></a>
    </div>
    <ion-infinite-scroll ng-if="hasMore" on-infinite="loadMoreContent()" spinner="spiral" distance="5" immediate-check="false"></ion-infinite-scroll>
</ion-content>

JavaScript:

JiCtrls.controller('homeCtrl', ['$scope', '$timeout', 'DbService', 'JsonService',
function ($scope, $timeout, DbService, JsonService) {
    $scope.items = [];
    $scope.hasMore = true;
    var run = false;
    loadData(0);
    //下拉更新
    $scope.loadNewContent = function () {
        loadData(2);
        // Stop the ion-refresher from spinning
        $scope.$broadcast("scroll.refreshComplete");
    };

    //上拉更新
    $scope.loadMoreContent = function () {
        loadData(1);
        $scope.$broadcast('scroll.infiniteScrollComplete');
    };




    function loadData(stateType) {
        if (!run) {
            run = true;
            if ($scope.sql == undefined) {
                $scope.sql = "select top 5 * from information ";
            }
            DbService.getData($scope.sql, '').success(function (data, status, headers, config) {
                var convertData = JsonService.convertData(data);
                if (stateType == 1 || stateType == 0) {
                    // $scope.items = $scope.items.concat(convertData);
                    for (var i = 0; i < convertData.length; i++) {
                        $scope.items.push(convertData[i]);
                    }

                }
                else {
                    for (var i = 0; i < convertData.length; i++) {
                        $scope.items.unshift(convertData[i]);
                    }

                }

                if (convertData == null || convertData.length <= 0) {
                    $scope.hasmore = false;
       ;
                }
                $timeout(function () {
                    run = false;
                }, 500);

            }).error(function (errorData, errorStatus, errorHeaders, errorConfig) {
                console.log(errorData);
            });
        }
    }
}

]);

Everything is normal in Chrome browser and Iphone, but in a part of the Android phone there is a big problem.When ion-refresher trigger on-refresh function,the on-infinite="loadMoreContent()" function will run indefinitely. So,What is the problem?

2 Answers2

1

Try to put $scope.$broadcast("scroll.refreshComplete"); and $scope.$broadcast('scroll.infiniteScrollComplete'); into DbService.getData(...).success() callback, not in functions triggered by on-infinite and on-refresh.

Explanation:

When the user scrolls to the end of the screen, $scope.loadMoreContent which is registered with on-infinite is triggered. The spinner shows, and ion-infinite-scroll pauses checking whether the user has reached the end of the screen, until $scope.$broadcast('scroll.infiniteScrollComplete'); is broadcast, when it hides the spinner and resumes the checking.

In your code, imagine there's a 3s delay in network, no new item is added to the list in that 3s. As a result, the inner height of ion-content is never updated, so the check that determines whether the user has reached the end of the screen will always return true. And you effectively prevent ion-infinite-scroll from pausing this checking by broadcasting scroll.infiniteScrollComplete the moment on-infinite is triggered. That's why it will update indefinitely.

To improve your code quality and prevent future problems, you may need to call $scope.$apply in your DbService.getData().success() callback (depending on the implementation of getData) and manually notify ion-content to resize in the callback.

P.S. 来自中国的Ionic开发者你好 :-) 两个中国人讲英语真累啊


Update

I've made a codepen that combines both ion-refresher and ion-infinite-scroll, I think it's working pretty fine.

http://codepen.io/KevinWang15/pen/xVQLPP

HTML

<html ng-app="ionicApp">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">

    <title>ion-refresher + ion-infinite-scroll 2</title>

    <link href="//code.ionicframework.com/nightly/css/ionic.css" rel="stylesheet">
    <script src="//code.ionicframework.com/nightly/js/ionic.bundle.js"></script>

  </head>
  <body ng-controller="MyCtrl">

    <ion-header-bar class="bar-positive">
      <h1 class="title">ion-refresher + ion-infinite-scroll 2</h1>
    </ion-header-bar>

    <ion-content delegate-handle="mainScroll">
      <ion-refresher on-refresh="doRefresh()">

      </ion-refresher>
      <ion-list>
        <ion-item ng-repeat="item in list">{{item}}</ion-item>
      </ion-list>

      <ion-infinite-scroll
        ng-if="hasMore"
        on-infinite="loadMore()"
        distance="1%">
      </ion-infinite-scroll>
    </ion-content>

  </body>
</html>

JS

angular.module('ionicApp', ['ionic'])

.controller('MyCtrl', function($scope, $timeout, $q, $ionicScrollDelegate) {
  /*
    list of items, used by ng-repeat
  */
  $scope.list = [];

  var itemOffset = 0,
    itemsPerPage = 5;

  /*
    used by ng-if on ion-infinite-scroll
  */
  $scope.hasMore = true;

  /*
    isRefreshing flag.
    When set to true, on data arrive
    it first empties the list 
    then appends new data to the list.
  */
  var isRefreshing = false;

  /* 
    introduce a custom dataFetcher instance
    so that the old fetch process can be aborted
    when the user refreshes the page.
  */
  var dataFetcher=null;

  /*
    returns a "dataFetcher" object
    with a promise and an abort() method

    when abort() is called, the promise will be rejected.
  */
  function fetchData(itemOffset, itemsPerPage) {
    var list = [];
    //isAborted flag
    var isAborted = false;
    var deferred = $q.defer();
    //simulate async response
    $timeout(function() {
      if (!isAborted) {
        //if not aborted

        //assume there are 22 items in all
        for (var i = itemOffset; i < itemOffset + itemsPerPage && i < 22; i++) {
          list.push("Item " + (i + 1) + "/22");
        }

        deferred.resolve(list);
      } else {
        //when aborted, reject, and don't append the out-dated new data to the list
        deferred.reject();
      }
    }, 1000);

    return {
      promise: deferred.promise,
      abort: function() {    
        //set isAborted flag to true so that the promise will be rejected, and no out-dated data will be appended to the list
        isAborted = true;
      }
    };
  }

  $scope.doRefresh = function() {
    //resets the flags and counters.
    $scope.hasMore = true;
    itemOffset = 0;
    isRefreshing = true;
    //aborts previous data fetcher
    if(!!dataFetcher) dataFetcher.abort();
    //triggers loadMore()
    $scope.loadMore();
  }

  $scope.loadMore = function() {

    //aborts previous data fetcher
    if(!!dataFetcher) dataFetcher.abort();

    //fetch new data
    dataFetcher=fetchData(itemOffset, itemsPerPage);

    dataFetcher.promise.then(function(list) {
      if (isRefreshing) {    
        //clear isRefreshing flag
        isRefreshing = false;
        //empty the list (delete old data) before appending new data to the end of the list.
        $scope.list.splice(0);
        //hide the spin
        $scope.$broadcast('scroll.refreshComplete');
      }

      //Check whether it has reached the end
      if (list.length < itemsPerPage) $scope.hasMore = false;

      //append new data to the list
      $scope.list = $scope.list.concat(list);

      //hides the spin
      $scope.$broadcast('scroll.infiniteScrollComplete');

      //notify ion-content to resize after inner height has changed.
      //so that it will trigger infinite scroll again if needed.
      $timeout(function(){
        $ionicScrollDelegate.$getByHandle('mainScroll').resize();
      });
    });

    //update itemOffset
    itemOffset += itemsPerPage;
  };

});
Kevin
  • 2,775
  • 4
  • 16
  • 27
  • Thank you very much! It is worked. But I found if I reached the end of the screen too fast, function loadMoreContent still load two times. So I rewrite like this: `$timeout(function() {$scope.$broadcast("scroll.refreshComplete");}, 500);` into `DbService.getData(...).success()` callback. 另外,好亲切啊,我是初学者,好多不懂,但是估计也只有你看得懂我的英文,哈哈 – 爬起来雨哥 May 02 '16 at 05:32
  • But it seems that these two components still have BUG, in a low version of the Android phone (In the apple mobile phone and other high version of Android is no problem). When I pull down the screen , the function loadNewContent() has triggered, but at the same time, the function loadMoreContent() has triggered too. Why? P.S.另外,万分感谢你,我终于搞清了它的原理 – 爬起来雨哥 May 02 '16 at 05:48
  • 哈哈 不客气~ ion-refresher should not be used to fetch new data and add them to the list. Instead, you should use it to clear all your list items (in your case, $scope.items), reset flags ($scope.hasMore) and counters. It should also notify ion-content to resize (refer to the link in my reply above). After ion-content is resized, ion-infinite-scroll will take care of loading external data into your list. – Kevin May 02 '16 at 09:13
  • http://codepen.io/KevinWang15/pen/NNEvpQ I've made a working code pen according to my comment, but this solution is still imperfect in that when the user refreshes the page, all items are removed from the list, so it flicks to white for some milliseconds, until new data is fetched by ion-infinite-scroll and shown on the screen. I should come up with another solution soon. – Kevin May 02 '16 at 10:02
  • Hey man, Can you help me about this?http://stackoverflow.com/questions/37477232/two-way-data-binding-in-ion-nav-view-of-ionic-not-workingwhy-bug – 爬起来雨哥 May 28 '16 at 06:37
0

The correct Javscript is as follows:

JiCtrls.controller('homeCtrl', ['$scope', '$timeout', '$ionicScrollDelegate', 'DbService', 'JsonService',
function ($scope, $timeout, $ionicScrollDelegate, DbService, JsonService) {
    $scope.items = [];
    $scope.hasMore = true;
    loadData(0);
    //下拉更新
    $scope.loadNewContent = function () {
        loadData(2);
    };
    //上拉更新
    $scope.loadMoreContent = function () {
        loadData(1);
    };



    function loadData(stateType) {

        if ($scope.sql == undefined) {
            $scope.sql = "select top 5 * from information”;
        }
        DbService.getData($scope.sql, '').success(function (data, status, headers, config) {
            var convertData = JsonService.convertData(data);
            if (stateType == 0) {
                for (var i = 0; i < convertData.length; i++) {
                    $scope.items.push(convertData[i]);
                }

            }
            else if (stateType == 1) {
                // $scope.items = $scope.items.concat(convertData);
                for (var i = 0; i < convertData.length; i++) {
                    $scope.items.push(convertData[i]);
                }

                $timeout(function () {
                    $scope.$broadcast('scroll.infiniteScrollComplete');
                }, 500);

            }
            else {
                for (var i = 0; i < convertData.length; i++) {
                    $scope.items.unshift(convertData[i]);
                }
                // Stop the ion-refresher from spinning
                $timeout(function () {
                    $scope.$broadcast("scroll.refreshComplete");
                }, 500);

            }
            $ionicScrollDelegate.resize();
            if (convertData == null || convertData.length <= 0) {
                $scope.hasmore = false;
            }

        }).error(function (errorData, errorStatus, errorHeaders, errorConfig) {
            console.log(errorData);
        });

    }
}

]);