How come Angular doesn't update with scope here?

ghz 1years ago ⋅ 226 views

Question

I'm pretty new to Angular and I'm using firebase as my backend. I was hoping someone could debug this issue. When I first go to my page www.mywebsite.com/#defaultHash the data doesn't load into the DOM, it does after visiting another hash link and coming back though.

My controller is like this:

/* initialize data */

var fb = new Firebase('https://asdf.firebaseio.com/');

/* set data to automatically update on change */

fb.on('value', function(snapshot) {

  var data = snapshot.val();
  $scope.propertyConfiguration = data.products;
  console.log($scope.propertyConfiguration);
  console.log("Data retrieved");

});

/* save data on button submit */

$scope.saveConfigs = function(){

  var setFBref = new Firebase('https://asdf.firebaseio.com/products');
  setFBref.update($scope.propertyConfiguration);
  console.log("configurations saved!");

};

I have 3 hash routes say "Shared", "Registration", and "Home" with otherwise.redirectTo set to "Shared".(They all use this controller) Here's the error that occurs: (all "links" are href="#hashWhereever")

  1. Go to website.com/#Shared or just refresh. Console logs $scope.propertyConfiguration and "Data Retrieved". DOM shows nothing.

  2. Click to website.com/#Registration , console logs $scope data properly, DOM is loaded correctly.

  3. Click back to website.com/#Shared , console logs $scope data properly yet this time DOM loads correctly.

  4. Refresh currently correctly loaded website.com/#Shared. DOM elements disappear.

Since $scope.data is correct in all the cases here, shouldn't Angular make sure the DOM reflects the model properly? Why is it that the DOM loads correctly only when I am clicking to the page from another link.

I can "fix" it by adding window.location.hash = "Shared" but it throws a huge amount of errors in the console.

FIXED :(sorta)

The function $scope.$apply() forces the view to sync with the model. I'd answer this question myself and close it but I'm still wondering why the view doesn't load correctly when I correctly assign a value to $scope. If Angular's "dirty checking" checks whenever there is a possibility the model has changed, doesn't assigning a value to $scope overqualify?


Answer

Angular has no way to know you've assigned a value to $scope.variable. There's no magic here. When you run a directive (ng-click/ng-submit) or Angular internal functions, they all call $apply() and trigger a digest (a check of the dirty flags and update routine).

A possibly safer approach than $apply would be to use $timeout. Currently, if you call a write op in Firebase, it could synchronously trigger an event listener (child_added, child_changed, value, etc). This could cause you to call $apply while still within a $apply scope. If you do this, an Error is thrown. $timeout bypasses this.

See [this SO Question](https://stackoverflow.com/questions/20765506/firebase- loop-look-up-data/20766868#20766868) for a bit more on the topic of digest and $timeout.

This doc in the Angular Developer Guide covers how compile works; very great background read for any serious Angular dev.

Also, you can save yourself a good deal of energy by using the official Firebase bindings for Angular, which already take all of these implementation details into account.

Vaguely Related Note: In the not-too-distant future, Angular will be able to take advantage of [Object.observe magic](http://updates.html5rocks.com/2012/11/Respond-to-change-with-Object- observe) to handle these updates.