0

My angular app has/needs two way data binding.

I fetch data from indexedDB using angular service, which is injected in a controller.

I need the view to be updated after the fetch from db is made.

var app = angular.module("app",["ngRoute"]);

app.config(['$routeProvider',function($routeProvider) {
    //removed extra routes for simplicity
    $routeProvider.when('/grouped',{
        templateUrl: 'template/grouped.html',
        }).otherwise({
        redirectTo: '/grouped'
    });
}]);

The controller in the same file

 app.controller("AppController",function ($scope,dexiedb) {
    $scope.datais={};
    $scope.dataisdata={};
    $scope.alldata = {};

    var scholar = {};

    var _db;
    window.initial = 0;
    var callback_alldata = function(err,data){
      if(!err){
      /* If I put apply it works*/
      // $scope.$apply(function() {
        $scope.alldata = data;
            // });
      }
    console.log('in callback_alldata ',err,data);
    };

    var init = function() {
      console.log('hi in init');
      dexiedb.getdata(callback_alldata);
      console.log($scope);
    };
  init(); 
 });

The service that fetches data from db

app.service('dexiedb',function(){

  var createdData = {};
  var _db = null;

  var service = {};
  //Initialization code which makes connection
  service.init = function(){

  _db = new Dexie('puppets');
  _db.version(1).stores({
        snips: "++id, text",
        parent: "++id, title, timeStamp"
    });

   _db.open().then(function(){
      service.createdata();
      console.log('service.init then called');
     }).catch(function(e){
      console.log('error opening',e);
            });
  };
  //The actual place where data is fetched and returned
   service.createdata = function(callback){
      console.log('createdata');
        var alldata = [];
      _db.transaction('rw',_db.parent, _db.snips, function(){

        _db.parent.each(function(par){
          var r = {'parent':par,'snips':[]};
          _db.snips.where('URL').equals(par.URL).each(function(snip){
              r.snips.push(snip);
          });
          alldata.push(r);
          // console.log(alldata);
        }).then(function(){
          createdData = alldata;
          console.log('createdata',createdData);
          return callback(null,createdData);
          // return createdData;
        });
      });
    };
//The method which is called in the controller
service.getdata = function(callback){
  // console.log(createdData);
  if (Object.keys(createdData).length==0) {
    console.log('createdData was empty');
    return service.createdata(callback);
  } else return callback(null,createdData);
}

service.init();

return service;

 });

I understand that $scope.apply should be used if the place where variable is being updated is not in angular scope. While in callback won't the angular scope be present?

The data is fetched and logged in console but it does not show up in the view until I click on another route and then come back again.

My understanding of promises/callback isn't solid yet, is the problem due to mishandling of callbacks?

avck
  • 3,535
  • 3
  • 26
  • 38
  • Why arent you using the native $http module for making requests? – Pytth Jun 17 '16 at 18:17
  • The db is in the browser itself. I do not need http requests – avck Jun 17 '16 at 18:19
  • Ahh I see. Well, you are on the right track with knowing you want to use promises — since you are still performing an async action. Take a look at using the Angular $q library for promises. – Pytth Jun 17 '16 at 18:21

1 Answers1

2

The issue is that Angular is not aware of the change made to $scope (i.e. $scope.alldata = data;).

Changes made to the $scope in your controller will be updated immediately, however your code is inside a callback. Angular reaches the end of the controller before the callback is fired, and so it misses the digest cycle. It cannot know your callback was invoked.

While you are updating the $scope in the callback, that doesn't trigger a new digest cycle.

dexiedb does not appear to be using $http, you will therefore need to use $scope.$apply in this case. You can see angular's implementation of $http which includes $apply.

Miles P
  • 710
  • 4
  • 12
  • Thanks, that makes sense. While using apply does the job. I also get error when clicking on another route `Error: $digest already in progress`. Using `$timeout` resolves it , can you explain what happens here? – avck Jun 17 '16 at 18:36
  • You'd get that error if for example you invoked $scope.$apply in your controller directly instead of a callback since digest is already in progress. More specifically, angular already knows it needs to trigger its watchers, so asking it to fire the watchers prematurely is not needed and could introduce many undesired side effects. $timeout however waits for the current digest to finish before invoking a new one. – Miles P Jun 17 '16 at 22:04