Thomas David Kehoe
Thomas David Kehoe

Reputation: 10930

Angular Firebase (AngularFire) CRUD SHOW and EDIT pages?

To learn AngularFire I'm trying to build a simple CRUD app. The INDEX and NEW pages went smoothly but I'm stuck on the SHOW and EDIT pages. I can't get a selected record to display on the SHOW page.

With MongoDB and Angular it's easy to make the SHOW page. On the INDEX page you put in an anchor link with the ID of the record you want to show:

<a ng-href="#/{{record._id}}"><span>{{record.title}}</span<</a>

This puts the record ID into the URL, e.g., www.website.com/#/K9hr32rhror

Then on the SHOW page you can use {{record.title}} or {{record.author}} or whatever you want. Angular magically remembers the ID of the record you clicked on the INDEX page. How Angular does this I have no idea. :-)

In MongoDB the ID is called _id. In Firebase the ID is called $id. So I changed the code on the INDEX page to

<a ng-href="#/{{record.$id}}"><span>{{record.title}}</span<</a>

This successfully puts the ID into the URL.

The problem is that no record is displayed on the SHOW page. Angular appears to have forgotten what {{record}} refers to.

I tried putting in a leading /:

<a ng-href="/#/{{record._id}}"><span>{{record.title}}</span<</a>

That makes no difference.

I tried parsing the ID from the URL with

var key = location.hash.split('#/')[1]

This returns the ID. When I console.log the $firebaseArray(ref) object I see a collection (array) of all my records. But when I use collection.$getRecord(key) or collection.$keyAt(0) or collection.$indexFor(key) I get null or -1, indicating that AngularFire can't find the record.

Here's my full controller code:

app.controller('ShowController', ['$scope', '$firebaseArray', '$location',
function($scope, $firebaseArray, $location) {

var ref = new Firebase("https://my-firebase.firebaseio.com/");
list = $firebaseArray(ref);
console.log(list); // []
console.log(location.hash.split('#/')[1]); // -K9hr32rhror_
var key = location.hash.split('#/')[1];
console.log(list.$getRecord(key)); // null
console.log(list.$keyAt(0)); // null
console.log(list.$indexFor(key)); // -1

Next I tried using ng-repeat="record in collection | filter : key"> but I can't figure out what key should be.

I also googled "Angular Firebase CRUD SHOW page" but none of the example CRUD apps had SHOW pages. Am I misunderstanding something fundamental? E.g., should I build the SHOW and EDIT views with <ng-hide> and <ng-show> on the INDEX page, and not bother to make separate SHOW and EDIT pages?

Upvotes: 2

Views: 1521

Answers (3)

Gaurav Nandankar
Gaurav Nandankar

Reputation: 21

    import { Injectable } from "@angular/core";
import { AngularFirestore } from "@angular/fire/firestore";
import User from "../models/user-model.model";

@Injectable({
  providedIn: "root",
})
export class FireStoreService {
  constructor(private _db: AngularFirestore) {
    fireStore = this;
  }
   
  public async userDataById(uid: string) {
    const userSnap = await this._db.doc(`users/${uid}`).get().toPromise();
    return userSnap.data();
  } 
  
  public async createUser(_id : string,userModel : User) {
    try {
      //Generate Firebase Id for New Node
      //Push User Data in that Node
      return this._db.collection('/User/').doc(_id).set({
        isDelete: userModel.isDelete,
        isActive: userModel.isActive
      });
    } catch(error) {
      console.log(error)
      return null;
    }
  }  
   
  public async getUsersListing() {
    try {
      // Query to get users where isDelete is false and isActive is true
      const usersListingSnapshot = await this._db.firestore.collection("/User/")
        .where("isDelete", "==", false)
        .where("isActive", "==", true) 
        .get();
      
      // Mapping the documents to data
      const usersListings = usersListingSnapshot.docs.map((doc) => doc.data());
      
      return usersListings;
    } catch (error) {
      console.error('Error getting user listings:', error);
      return null;
    }
  }
  
    
  public async getCaptionDetailsById(Id: string) {
    try { 
      const querySnapshot = (await this._db.firestore.collection("/User/").get()).docs
        .filter((doc) => doc.id === Id)
        .map((doc) => doc.data());
      return querySnapshot;
    } catch (error) {
      return null;
    }
  }

  
  public async updateUserDetails(userModel: UserModel): Promise<boolean> {
    try {
      const result = await this._db.collection('/User/').doc(userModel.id).update({
        name: userModel.name,
        number: userModel.number,
        gender: userModel.gender,
        isVerify: userModel.isVerify,
        updateAt: userModel.updateAt,
        photo: userModel.photo,
      });
  
      return true;
    } catch (error) {
      console.error('Error updating user details:', error);
      return false;
    }
  }
  
  

  public async deleteData(id: string): Promise<Boolean> {
    try {
        // Mark the document as deleted instead of actually deleting it
        await this._db.collection('/RouteMaster/').doc(id).update({ isDelete: true,  isActive: false   });
        console.log(`Document with ID ${priceId} marked as deleted successfully`);
        return true;
    } catch (error) {
        console.error('Error marking document as deleted:', error);
        return false;
    }
}
  
  
}

Upvotes: 0

Thomas David Kehoe
Thomas David Kehoe

Reputation: 10930

Thanks, David East, the problem was asynchronous data. Here are two working controllers. The first controller gets the data array and selects the record I want out of the array:

app.controller('ShowController', ['$scope', '$firebaseArray', '$location',
function($scope, $firebaseArray, $location) {
  // set up Firebase
  var ref = new Firebase("https://my-firebase.firebaseio.com/"); // goes to Firebase URL (fbURL)
  var list = $firebaseArray(ref); // puts
  list.$loaded().then(function(x) { // promise that executes when asynchronous data has loaded
    var key = location.hash.split('#/')[1]; // parses key from URL query string
    var showItem = list.$getRecord(key); // gets the record using the key passed in from the URL query string
    var showItems = []; // make an array
    showItems.push(showItem); // push the record into the array
    $scope.showItems = showItems; // attach the array to $scope
    // now 'ng-repeat="showIdea in showIdeas"' will iterate through the array, which has only one element
    // i.e., this is how you make Angular display one thing
  })
  .catch(function(error) { // if the data doesn't load
    console.log("Error:", error);
  });
}]);

The second controller uses David East's suggestion of using $firebaseObject to download only the record I want. I added some code to make it display using ng-repeat. Is there another way to display a single record with Angular?

app.controller('ShowController', ['$scope', '$firebaseObject', '$location',
function($scope, $firebaseObject, $location) {
  // set up Firebase
  var ref = new Firebase("https://my-firebase.firebaseio.com/"); // goes to Firebase URL (fbURL)
  var key = location.hash.split('#/')[1]; // parses key from URL query string
  var itemRef = ref.child(key); // sets up the query
  var showItems = []; // make an array
  showItems.push($firebaseObject(itemRef)); // makes the query and push the record into the array
  $scope.showItems = showItems; // attach the array to $scope
}]);

And here's the view:

<table ng-repeat="showItem in showItems">
  <tr><td>Name:</td><td>{{showItem.itemName}}</td></tr>
  <tr><td>Title:</td><td>{{showItem.itemTitle}}</td></tr>
</table>

Upvotes: 1

David East
David East

Reputation: 32604

The problem you're dealing with is asynchronous data flow.

app.controller('ShowController', ['$scope', '$firebaseArray', '$location',
function($scope, $firebaseArray, $location) {
  var ref = new Firebase("https://my-firebase.firebaseio.com/");
  var key = location.hash.split('#/')[1];
  $scope.list = $firebaseArray(ref); // data is downloading
  console.log($scope.list.length); // will be undefined, data is downloading
  $scope.list.$loaded().then(function(list) {
    console.log(list); // data has been downloaded!
    list.$getRecord(key); // data is available
  });
});

But .$loaded() is usually unnecessary because AngularFire knows when to trigger $digest. In your case you want to use a $firebaseObject.

app.controller('ShowController', ['$scope', '$firebaseObject', '$location',
function($scope, $firebaseObject, $location) {
  var ref = new Firebase("https://my-firebase.firebaseio.com/");
  var key = location.hash.split('#/')[1];
  var itemRef = ref.child(key);
  $scope.item = $firebaseObject(itemRef); 
});

This grabs the single item based off of the key, rather than downloading the entire list and plucking out one item.

Upvotes: 3

Related Questions