0

OK switching my code to angularjs and the angular 'way', not sure what I am doing wrong.

A select list is not getting updated when the model changes unless I call $apply, and I find myself calling apply a lot.

index.html has this:

<div id='rightcol' data-ng-include="'partials/rightSidebar.html'"  
    data-ng-controller="rightSidebarController">
</div>

and rightSidebar.html has this:

<select id='srcList' size='10'
        data-ng-model="data.source" 
        data-ng-click='srcOnclick()'
        data-ng-options="s.title for s in data.srcList | filter:{title:data.srcFilter} | orderBy:'title'"></select>

rightSidebarController.js has this:

$scope.data = {};
$scope.data.srcList = dataProvider.getSourceList();
$scope.data.source = dataProvider.getSource();

dataProvider is a service that makes an asynchronous database call (IndexedDB) to populate srcList, which is what gets returned in dataProvider.getSource().

Is it the asynchronous database call that forces me to call $apply, or should the controller be ignorant of that?

Is there a 'better' way to do this?

Edited to add service code.

Another controller calls dataProvider.refreshSourceList:

myDB.refreshSourceList = function() {
    myDB.getRecords("source", function(recs) {
        myDB.srcList = recs;
        $rootScope.$broadcast('SrcListRefresh');
    });
};

myDB.srcList is the field being bound by $scope.data.srcList = dataProvider.getSourceList();

myDB.getRecords:

myDB.getRecords = function(storeName, callback) {
    var db = myDB.db;
    var recList = [];
    var trans = db.transaction([storeName], 'readonly');
    var store = trans.objectStore(storeName);

    var cursorRequest = store.openCursor();
    cursorRequest.onerror = myDB.onerror;

    cursorRequest.onsuccess = function(e) {
        var cursor = cursorRequest.result || e.result;
        if (cursor === false || cursor === undefined) {
            if (callback !== undefined) {
                $rootScope.$apply(function() {
                    callback(recList);
                });
            }
        } else if (cursor.value !== null) {
            recList.push(cursor.value);
            cursor.continue();
        }
    };

    cursorRequest.onerror = myDB.onerror;
};
5
  • Show where the model is changing and its not updating the view Commented Jul 9, 2013 at 13:45
  • You have to call $apply if you are not using $resource or $http -- i.e., if you are doing something "outside" of Angular you need to call $apply so that Angular will run a digest cycle and check all of the $watches that have been set up. Commented Jul 9, 2013 at 13:51
  • Mark, my originally implementation generates an event when the field changes, and interested parties are calling the $apply. I instinctively sense that is not the angular way though, and I get $apply already in progress msgs frequently. Commented Jul 9, 2013 at 14:22
  • James I updated the code as requested Commented Jul 9, 2013 at 14:26
  • The issue has been resolved - I misunderstoond pass by reference meant pass a copy of a reference. I have re designed my app accordingly. Commented Jul 17, 2013 at 14:56

1 Answer 1

1

Anything you do async needs to be wrapped in $scope.$apply(). This is because angular works in a similar fashion to a game loop, however instead of constantly running, it knows to end the loop when an action is taken, and $scope.$digest() is called.

If you are using IndexedDB, I would recommend creating an angular wrapper for it, like so: (forgive my IndexedDB code, I'm not experience with it)

angular.module('app',[])
    .factory('appdb', function($rootScope){
        var db = indexedDB.open('appdb', 3);
        return {
            get : function(table, query, callback) {
                var req = db.transaction([table])
                    .objectStore(table)
                    .get(query);

                req.onsuccess(function(){
                    $rootScope.$apply(function(){
                        callback(req.result);
                    });
                });
            }
        };
    });

This way you can be sure that any data retrieve and set on a controller scope inside of callback will have $scope.$digest() called afterward.

Sign up to request clarification or add additional context in comments.

2 Comments

I see no change in behavior when I 1) wrap the callback in an $apply or 2) implement a promise that is resolved onsuccess.
INtersting, if I link direct to srcList instead of calling getSourceList(), it works - sort of - still have to reset the variable in an $on call

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.