EMRADE
EMRADE

Reputation: 183

What is the proper way to use transactions in firebase with Angular

I normally use AngularFire2 when working with Firebase. But this time i did not, i used the normal Firebase library because when i checked the docs on AngularFire2 for the usage of transaction which i need, i didn't find it.

Using the normal Firebase library i came across this error which says the transaction wasn't committed, but the data still saves to Firebase. Can anyone please point out the mistake i have made?

Here is my code:

saveSeats(key, numbers){
  for (let i = 0; i < numbers.length; i++) {
    const num = numbers[i].toString();
    const objPath = `${this.basePath}/${key}/${num}`;      
    const busesRef = firebase.database().ref("buses/"+key+"/"+num); 

    busesRef.transaction(function(currentValue) {
      if (currentValue === null) {
        let newRow = JSON.stringify({ number: num, status: "reserved", timestamp: Date.now() });
        busesRef.child("0").set(newRow);
      } else {
        console.log('Value Already Exists');
      }    
    }, function(error, committed, snapshot) {
      if (error) {
        console.log('Transaction failed abnormally!', error);
      }else if (!committed) {
        console.log('We aborted the transaction (because number already exists).');
      } else {
        console.log('Number added!');
      }
      console.log("Number: ", snapshot.val());      
    });

  }

}

Upvotes: 0

Views: 1027

Answers (1)

Renaud Tarnec
Renaud Tarnec

Reputation: 83163

You should not use the set() method in your transaction, as presented in the doc:

Note: Modifying data with set() will cancel any pending transactions at that location, so extreme care should be taken if mixing set() and transaction() to update the same data.

You should "pass transaction() an update function which is used to transform the current value into a new value."

So the following should normally work:

  busesRef.transaction(function(currentValue) {
    if (currentValue === null) {
      let newRow = JSON.stringify({ number: num, status: "reserved", timestamp: Date.now() });
      return newRow;  // <- Here the function returns the new value
    } else {
      console.log('Value Already Exists');
      return; // Will abort the transaction.
    }    
  },
  ....

Finally, I think that you don't need to stringify your object. Doing the following should work:

      let newRow = { number: num, status: "reserved", timestamp: Date.now() };
      return newRow;

or, of course, simply

      return { number: num, status: "reserved", timestamp: Date.now() };

A further consideration:

I've noticed that you wrap your transactions within a for loop, meaning that you will modify the values of different busesRef nodes. Shouldn't you do all these modifications within ONE transaction? And not with a set of "atomic" transactions, that each modifies the value of a given busesRef node? It depends on your exact use case.

Upvotes: 1

Related Questions