Stack Overflow is a community of 4.7 million programmers, just like you, helping each other.

Join them; it only takes a minute:

Sign up
Join the Stack Overflow community to:
  1. Ask programming questions
  2. Answer and help your peers
  3. Get recognized for your expertise

I have this mobile app where it lists a record of encoded transport/taxi receipt. The first page seen when opening the app is the list of receipt.

templates/list-view.html

<ion-item class="card item item-light" ng-click="edit({{receipt.id}})" ng-repeat="receipt in receipts|orderBy:'receiptDate'">
   ...
</ion-item>
   ...
<ion-footer-bar class="bar-subfooter taxi-subfooter">
<div class="row">
    <div class="col">
        <button ng-click="addRecord(-1)" class="button button-block"><i class="icon ion-plus-round"></i></button>
    </div>
</div>

if I click the item or if I click the button with addRecord(-1), it will go to the add/edit receipt page:

templates/edit-receipt.html

<ion-content padding="true" class="has-header has-footer taxi-has-subfooter">
    <div class="table">
        <div class="row">
            <div class="col title">Paper receipt provided?</div>
            <div class="col taxi-col">
                <label class="toggle toggle-balanced">
                    <input type="checkbox" ng-model="bindReceipt.hasReceipt">                       
                    <div class="track taxi-track">
                        <div class="handle taxi-handle"></div>
                    </div>
                </label>
                <input type="hidden" ng-model="bindReceipt.id" />
            </div>
        </div>
        <div class="row">
            <div class="col title">Date</div>
            <div class="col taxi-col"><input type="date" ng-model="bindReceipt.receiptDate" /></div>
        </div>
        <div class="row">
            <div class="col title">Vendor</div>
            <div class="col taxi-col"><input type="text" placeholder="Name of the Taxi" ng-model="bindReceipt.taxiVendor" /></div>
        </div>
        <div class="row">
            <div class="col title">Fare</div>
            <div class="col taxi-col"><input type="tel" placeholder="0.00" ng-model="bindReceipt.fare" /></div>
        </div>
    </div>
</ion-content>
<ion-footer-bar class="bar-subfooter taxi-subfooter">
        <div class="row"> <!-- update() functions also as add -->
            <div class="col taxi-col"><button class="button button-block" ng-click="update()">{{transType}}</button></div>
        </div>
    </ion-footer-bar>

What I did in its behavior for back button of the phone is if the state is on the add/edit page, it will just go back to the list page, and if the state is on the list page, no matter how many times the user will go from/to list and edit pages, it should exit the app (no going back of the app history). So, I used navigator.app.exitApp() for the backbutton on the list page.

templates/list-view.html uses ReceiptsCtrl controller so I put there my event listener for backbutton:

app.controller('ReceiptsCtrl', function($window, $scope, $filter, $state, $http, $ionicPopup, Receipts) {

    $scope.receipts = Receipts.getReceipts();

    $scope.historyBack = function(evt) {
        if(evt != null) {
          if(evt.preventDefault) {
            evt.preventDefault();
          }
        }

        var location = $window.location.href;
        if(location.charAt(location.length-1) === '/') {

            navigator.app.exitApp(); // exit the application
        } else {
            history.go(-1);
        }

      }

      document.addEventListener('backbutton', $scope.historyBack, false);

});

services.js

.factory('Receipts', function($window, $filter) {

  var receipts = JSON.parse($window.localStorage.getItem('receipts')) || [];
  return {
    getReceipts: function() {return receipts;}

  }

});

Here's my problem, I have this scenario in my app after I build and run in android:

  1. opening the app. 0 record initially
  2. add two items - 2 records in the list page
  3. currently on the list page then exit the app (navigator.app.exitApp() triggers)
  4. open the app again, 2 records is still there
  5. add one item - 3 records in the list page
  6. on the list page again then exit app
  7. open the app, missing list

Screenshot of the scenario here

That is not happening when I do not use the backbutton event listener, but I find it annoying if it go through history swinging between list and edit page before exiting the app.

I use $window.localStorage for storing data then pass it on $scope.receipts for the list ng-repeat, I tried debugging it by inserting alert() with the receipt object fetched from localStorage as argument. There is a value on the alert message but listing is not working. Another thing also is that it can still compute the total fare from the $scope.receipts (as stated in the screenshot also).

Many thanks.

share|improve this question
    
can you make a plunker plz ? – macrog Nov 11 '15 at 10:47
up vote 0 down vote accepted

I think I found the issue. I have no idea how to replicate it on browser but I was able to catch it in Android Device Monitor

11-12 03:08:23.861: I/chromium(30444): [INFO:CONSOLE(139)] "Error: [ngRepeat:dupes] http://errors.angularjs.org/1.4.3/ngRepeat/dupes?p0=[Some data from my app URL encoded. It's too long]
11-12 03:08:23.861: I/chromium(30444):     at Error (<anonymous>)
11-12 03:08:23.861: I/chromium(30444):     at file:///android_asset/www/lib/ionic/js/ionic.bundle.min.js:37:416
11-12 03:08:23.861: I/chromium(30444):     at file:///android_asset/www/lib/ionic/js/ionic.bundle.min.js:309:261
11-12 03:08:23.861: I/chromium(30444):     at Object.fn (file:///android_asset/www/lib/ionic/js/ionic.bundle.min.js:162:168)
11-12 03:08:23.861: I/chromium(30444):     at n.$digest (file:///android_asset/www/lib/ionic/js/ionic.bundle.min.js:163:259)
11-12 03:08:23.861: I/chromium(30444):     at n.$apply (file:///android_asset/www/lib/ionic/js/ionic.bundle.min.js:166:269)
11-12 03:08:23.861: I/chromium(30444):     at l (file:///android_asset/www/lib/ionic/js/ionic.bundle.min.js:118:152)
11-12 03:08:23.861: I/chromium(30444):     at F (file:///android_asset/www/lib/ionic/js/ionic.bundle.min.js:122:187)
11-12 03:08:23.861: I/chromium(30444):     at XMLHttpRequest.K.onload (file:///android_asset/www/lib/ionic/js/ionic.bundle.min.js:123:220)", source: file:///android_asset/www/lib/ionic/js/ionic.bundle.min.js (139)

I searched about ngRepeat:dupes - AngularJS doesn't allow duplicates and recommended to use track by to avoid errors like this. So I changed my code and use it on ng-repeat.

With correct format, I wrote:

ng-repeat="receipt in receipts|orderBy:'receiptDate'| filter:receiptFilter track by receipt.id"

I tried track by right after the orderBy but does not work. I found that it should be after a filter in order to work. So I made a receiptFilter variable under my controller (something empty string like $scope.receiptFilter = "";) just to have something harmless in my result of ng-repeat.

Before resorting to track by receipt.id, I tried track by $index but the error persists. I look for answers and found that it is not safe to use $index when using orderBy.

In summary, when sorting (orderBy), use track by <a unique key> (not $index).

I hope this answer will help and any more supplements on this one will be much appreciated. :)

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.