Asked  7 Days ago    Answers:  5   Viewed   13 times

I am having some trouble using AngularFire / Firebase to get an individual record from a collection (and then, after I have it, $remove'ing it from the collection). I believe I'm following proper documentation located here: https://www.firebase.com/docs/web/libraries/angular/api.html#angularfire-firebasearray

NOTE: a lot of documentation / blogs / answers I have found appear to use the deprecated $firebase service (with the $asArray method) - however, more recent docs identify that $firebaseArray is the correct service to use, FYI.

For some reason, even though I can accurately pull my polls collection, I cannot extract an individual element (although the official docs appear to have the same syntax I'm using).

Docs for $remove (https://www.firebase.com/docs/web/libraries/angular/api.html#angularfire-firebasearray-removerecordorindex):

## official docs ##

$remove(recordOrIndex)
Remove a record from Firebase and from the local data. This method returns 
a promise that resolves after the record is deleted at the server. It will 
contain a Firebase reference to the deleted record. It accepts either an 
array index or a reference to an item that exists in the array.

var list = $firebaseArray(ref);
var item = list[2];
list.$remove(item).then(function(ref) {
  ref.key() === item.$id; // true
});

My code (see console.log's below):

.service('pollsData', function($firebaseArray){
    var fireRef = new Firebase('<-my-firebase-ref-url>');
    var polls = $firebaseArray(fireRef.child('polls'));
    var service = this;

    this.clearPolls = clearPolls;
    this.setInitialPolls = setInitialPolls;
    this.getPolls = getPolls;

    function clearPolls() {
      console.log("length: ", polls.length);  <--- I get a length of 0
      console.log(polls);  <-- I get a $firebaseArray object including the methods in the API docs
      console.log(polls[0], polls[1], polls[2]); <--- all 3 are undefined even though I can see several in the firebase data dashboard

      var poll = polls[1];
      console.log("poll?: ", poll);
      polls.$remove(poll);
    }

    function getPolls() {
      polls = polls || $firebaseArray(fireRef.child('polls'));
      return polls;  <-- I can successfully ng-repeat over this returned selection in the template
    }

    function setInitialPolls() {
      service.clearPolls();
      polls.$add({
        one: {
          name: "fantastic pollllllls",
          selection_one: {
            name: "pizza",
            count: 0
          },
          selection_two: {
            name: "fries",
            count: 0
          }
        },
        two: {
          name: "mediocre poll",
          selection_one: {
            name: "blue",
            count: 0
          },
          selection_two: {
            name: "green",
            count: 0
          }
        }
      });
    }

    setInitialPolls();
});

Any insights as to why the brackets [] notation to pull out a record from the $firebaseArray would not work as expected per the AngularFire documentation?

Updated: the responses provided were still helpful since this appeared to be in conflict with my understanding of the newer documentation for $firebaseArray and the example given for the $remove section in the API docs. Understand this is still fundamentally an async concern, but since the $firebase service was deprecated and I was referencing the updated docs, this updated Question/Solution felt necessary.

 Answers

6

There are three methods to retrieve a record out of the $firebaseArray object.

  1. $getRecord(key)
  2. $keyAt(recordOrIndex)
  3. $indexFor(key)

I would look more closely at the documentation on each of these, but from what you have described, you probably want to use $keyAt. Keep in mind, though, that $firebaseArray is not a strict array, and the available methods provide more options/flexibility than what you would be used to having in 'just an array'.

Also, as mentioned in the comments, you want to work inside of a promise method to handle the three way data bind for you.

var arrayRef = new Firebase(firebase_location).child(child_location);
var theList = $firebase(arrayRef).$asArray();
theList.$loaded(function (list) {
  var x = list.$keyAt(list[target_position]);

  //use other methods for the $firebaseArray object
});
Saturday, May 14, 2022
 
3

Note: most current browsers support HTML <template> elements, which provide a more reliable way of turning creating elements from strings. See Mark Amery's answer below for details.

For older browsers, and node/jsdom: (which doesn't yet support <template> elements at the time of writing), use the following method. It's the same thing the libraries use to do to get DOM elements from an HTML string (with some extra work for IE to work around bugs with its implementation of innerHTML):

function createElementFromHTML(htmlString) {
  var div = document.createElement('div');
  div.innerHTML = htmlString.trim();

  // Change this to div.childNodes to support multiple top-level nodes
  return div.firstChild; 
}

Note that unlike HTML templates this won't work for some elements that cannot legally be children of a <div>, such as <td>s.

If you're already using a library, I would recommend you stick to the library-approved method of creating elements from HTML strings:

  • Prototype has this feature built-into its update() method.
  • jQuery has it implemented in its jQuery(html) and jQuery.parseHTML methods.
Tuesday, June 1, 2021
 
Sendy
 
5

but that does not seem to be correct syntax.

It is not the correct syntax because you are missing a collection() call. You cannot call document() directly on your Firestore.instance. To solve this, you should use something like this:

var document = await Firestore.instance.collection('COLLECTION_NAME').document('TESTID1');
document.get() => then(function(document) {
    print(document("name"));
});

Or in more simpler way:

var document = await Firestore.instance.document('COLLECTION_NAME/TESTID1');
document.get() => then(function(document) {
    print(document("name"));
});

If you want to get data in realtime, please use the following code:

Widget build(BuildContext context) {
  return new StreamBuilder(
      stream: Firestore.instance.collection('COLLECTION_NAME').document('TESTID1').snapshots(),
      builder: (context, snapshot) {
        if (!snapshot.hasData) {
          return new Text("Loading");
        }
        var userDocument = snapshot.data;
        return new Text(userDocument["name"]);
      }
  );
}

It will help you set also the name to a text view.

Tuesday, July 13, 2021
 
sohum
 
2

This is because by the time your console.log($scope.item.url); runs, the data hasn't been loaded from Firebase yet. Angular listens for a notification from Firebase/AngularFire to know when the data has loaded and then updates the view.

Also see angularfire - why can't I loop over array returned by $asArray? and Trying to get child records from Firebase.

Tuesday, July 27, 2021
 
pwaring
 
4

See also this post, which provides a directive to do this grouping: Is it possible to .sort(compare) and .reverse an array in angularfire?

There are a few approaches you could take here.

Data Structured by Date

If the records will always be fetched by this structure, you can just store them that way;

/users/usera/tasks/week1/monday/taska/...

Then you can simply fetch them at the tasks level as an object, and you will obtain a pre-sorted JSON object with values nested at the appropriate levels.

This approach is not highly compatible with AngularFire, which is intended for binding objects or collections, not nested trees of data, but should work with some care.

Using Priorities

A second approach would be to add priorities on to the tasks, which would be an epoch timestamp. Now when you want to fetch them, you can startAt/endAt specific points in the tree, and get the records. Each will have a timestamp on it you can use to identify the week and day.

var ref = new Firebase(ref).startAt(twoWeeksAgo).endAt(nextThursday);
$scope.list = $fireabse(ref).$asArray();

However, this does not segment the entries. You would need to either examine each entry in the ng-repeat and add headers on the fly:

// in the controller
var last = null;
$scope.priorityChanged(priority) {
   /** 
    * here we would use a library like moment.js to parse the priority as a date
    * and then do a comparison to see if two elements are for the same day, such as 
    **/
   var current = moment(priority).startOf('day');
   var changed = last === null || !last.isSame(current);
   last = current;
   return changed;
};

$scope.getDayName = function($priority) {
   return moment($priority).format('dddd');
};

<!-- in the view -->
<li ng-repeat="item in list" ng-init="changed = priorityChanged(item.$priority)">
   <h3 ng-show="changed">{{getDayName(item.$priority)}}</h3>
   {{item|json}}
</li>

This approach is readily compatible with AngularFire.

Roll Your Own List

A last approach would be to cut out AngularFire and roll your own. For example, if we have the week and weekday stored on each task, we could do the following:

app.service('DatedList', function($timeout) {
   return function(pathToList) {
      var list = {};

      pathToList.on('child_added', function(snap) {
         $timeout(function() { // force Angular to run $digest when changes occur
            var data = snap.val();
            var week_number = data.week;
            var week_day = data.day;
            list[week_number][week_day] = data;
         });
      });

      //todo: write similar processing for child_changed and child_removed

      return list;
   }
});

app.controller('ctrl', function($scope, DatedList) {
   var listRef = new Firebase(URL).limit(500);
   $scope.weeks = DatedList(listRef);
});

<div controller="ctrl">
  <div ng-repeat="(week, days) in weeks">    
     <h1>{{week}}</h1>
     <div ng-repeat="(day, items) in days">
        <h2>{{day}}</h2>
        <div ng-repeat="item in items">
           {{item|json}}
        </div>
     </div>
  </div>
</div>
Thursday, August 19, 2021
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share