Victor Maricato
Victor Maricato

Reputation: 812

App Fails First Load When Listing Array's Item from a DataSnapshot from Firebase

I am storing a DataSnapshot into a $scope array, so the ng-repeat into HTML div updates my "result list"

The problem is, when I run the code, input a value and click to run the DataSnapshot function, the first result doesn't appear on the app screen, only on the app's log on browser's console. If I run the function again, the result appears. If I change the input and click the button (run the function) again, this input appears in the first try.

So, here's what you will probably need:

Print from first attempt(data appears on console but not on app): enter image description here Print from second attempt(data appears twice on console and once on app): enter image description here Print from third attempt with another input(data appears once on console and once on app): enter image description here

Codes:

index.html

<div class="list card">
    <div class="item item-body" ng-repeat="locker in lockers">
    <a  href="#" class="item item-icon-right text-center">
    <img ng-src="{{imageSrc}}" style="width: 35px; height: auto;"> 
    <p style="text-align: center">  
     Locker - {{locker.number}}
     <br>
     {{key}}                     
    </p>
    <i class="icon ion-chevron-right"></i>  
      </a>

    </div>

app.js

angular.module('starter', ['ionic', 'firebase'])

.controller('LockerCtrl', ["$scope", "$firebaseArray", function($scope,$firebaseArray, snapshot){
var lockersRef = new Firebase('https://ifrjarmariosdb.firebaseio.com/armarios');



$scope.getButtonClicked = function() {
  var lockernumber = document.getElementById('lockerNumberInput').value;
  if(lockernumber.length > 0){
    lockerInputedNumber = parseInt(lockernumber); 
    lockersRef.on('value', function(snapshot){ 

      snapshot.forEach(function(data){
        var number = data.val().number;

        if(number == lockerInputedNumber){ 
          $scope.key = data.key(); 
          $scope.lockers = [data.val()];
          console.log(data.val()); 

          if(number == 101){ -
            $scope.imageSrc = "img/locker_test2.png";
          }
          else{
            $scope.imageSrc = "img/locker_test.jpg";
          }
        }
      });
    });
  }

As you could see by the prints, I'm also facing some trouble to change the image source according to the number value from Firebase. If you could help me to solve that, it would be a great help.

Please, I'm not asking for the solution for this method, if you know a different method to do this, I ask you to post it too.

Thanks!

Upvotes: 0

Views: 602

Answers (1)

Frank van Puffelen
Frank van Puffelen

Reputation: 599581

This code starts loading data from Firebase:

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

  snapshot.forEach(function(data){
    var number = data.val().number;

    if(number == lockerInputedNumber){ 
      $scope.key = data.key(); 
      $scope.lockers = [data.val()];
      console.log(data.val()); 

      if(number == 101){ -
        $scope.imageSrc = "img/locker_test2.png";
      }
      else{
        $scope.imageSrc = "img/locker_test.jpg";
      }
    }
  });

The loading happens asynchronously. Since it takes time before the data is available, the browser continues executing the JavaScript after this code.

When the data comes back from the server, it executes your callback function. But at that point, AngularJS is no longer expecting any changes to the $scope.

The solution is to make AngularJS aware of the fact that you've changed the scope. The easiest way to do that, is to wrap the callback into a $timeout() call:

lockersRef.on('value', function(snapshot){ 
  $timeout(function() {
    snapshot.forEach(function(data){
      var number = data.val().number;

      if(number == lockerInputedNumber){ 
        $scope.key = data.key(); 
        $scope.lockers = [data.val()];
        console.log(data.val()); 

        if(number == 101){ -
          $scope.imageSrc = "img/locker_test2.png";
        }
        else{
          $scope.imageSrc = "img/locker_test.jpg";
        }
      }
    });
  });

Some people that ran into the same problem:

A few other things I note about your code:

  • you're downloading all lockers and then filtering for the one the user entered client-side. This is wasting bandwidth that your user might be paying for. A better way would be to leave the filtering to Firebase with a Query:

    var query = lockersRef.orderByChild('number').equalTo(lockerInputedNumber);
    query.on('value', function(snapshot){ 
    
  • there is a binding library for AngularJS+Firebase called AngularFire, which handles the $timeout() thing automatically. It's built on top of Firebase's JavaScript SDK that you're now using, so they interoperate perfectly.

Upvotes: 2

Related Questions