0

I'm trying to show the categories of a service using AngularJS and firebase with the ng-if directive by testing if the entry exist in a table.

The tables are written in a denormalize way as recommended on the Firebase guideline.

- servicecat
    - catid
        - name : "Category Name"
        - services
            - srvid1 : true
            - srvid2 : true
            - srvid3 : true
- services
   - srvid
        - name: "Service Name"
        - Details: "blabla"
        - servicecat:
            - catid1 : true
            - catid2 : true

I am trying to show on a page of a service the categories of the table where the key of the service exists.

    <li
      ng-repeat="cat in servicecat"
      ng-if="checkIfTrue(cat.$id, postSrv.$id)">
      {{ cat.name }}<br>
    </li>

So I'm trying to get the value a srvid ("true") and return it with the function checkIfTrue(catid,srvid).

$scope.checkIfTrue = function(catid,srvid) {
  var ref = fb.child('servicecat/'+catid+'/services/'+srvid);
  ref.once('value').then(function(snap){ 
    return snap.exists();
  });
}

I don't understand why the ng-if directive does not take the result of the function (which is true when the element exists in the table).

I can see that if I had a "return true;" at the end of the function (out of the ref.once('value') function), it is taken by the ng-if directive.

I was then trying to drop the result of the "once" function in a global variable that I could return at the end of the checkIfTrue function. It seems that the return on the "once" function is a "promise" that the ng-if directive can't read.

So I manage to fetch all categories and all services with the following factory. But when I try to get the selected categories of a specific sercices, It seems I miss something. I was trying to do it by joining two tables but It seems that I shouldn't do this method. Anyway I can't manage to solve this issue.

app.factory("getServices", ["$firebaseArray",
  function($firebaseArray) {
    var ref = firebase.database().ref().child("services");
    return $firebaseArray(ref);
  }
]);

app.factory("getServiceCategories", ["$firebaseArray",
  function($firebaseArray) {
    var ref = firebase.database().ref().child("servicecat");
    return $firebaseArray(ref);
  }
]);

app.controller("maincontroller", ["$scope","$firebaseArray","$firebaseObject",
"getServices","getServiceCategories",
  function($scope,$firebaseArray,$firebaseObject,  getServices, getServiceCategories) 
  {
    // GET ALL SERVICES AND ALL CATEGORIES
    $scope.services = getServices;
    $scope.servicecat = getServiceCategories;
    
    // SHOW & EDIT SERVICE DETAILS AND CATEGORIES
    $scope.showDetailsService = function(srvid) {
       var ref = fb.child('services/'+srvid);
       $scope.selectSrvtoPost = $firebaseObject(ref);
       var ref2 = fb.child('services/'+srvid+'/servicecat/');
       $scope.selectSrvCatID = $firebaseArray(ref2);
       var result = ref2.once('value', function(snap) {
       snap.forEach(function(snap2) {
         var catid = snap2.key;
         var ref3 = fb.child('servicecat/'+catid);
         ref3.once('value', function(snap3) {
           var catname = snap3.val().name;
           console.log("Srvid: "+srvid+" /// Id : "+catid+" /// Name : "+catname);
           return snap3;
        })
      })
    })
    // ANOTHER TRY WITH $loaded()
    var ref2 = fb.child('services/'+srvid+'/servicecat/');
    $scope.listcat = $firebaseArray(ref2);
    $scope.listcat.$loaded()
    .then(function(snap) {
      snap.forEach(function(snap2) {
        var catid = snap2.key;
        var ref3 = fb.child('servicecat/'+catid);
        ref3.once('value', function(snap3) {
          var catname = snap3.val().name;
          console.log("Srvid: "+srvid+" /// Id : "+catid+" /// Name : "+catname);
          return snap3;
        })
      })
    })

}
 )];
<div ng-controller="mainController">
    <div class="mdl-grid">
      <div class="mdl-cell mdl-cell--3-col">
        <h3>List Services</h3>
        <ul>
          <li ng-repeat="service in services" ng-click="showDetailsService(service.$id)" class="clickable"><strong>{{ service.title }}</strong><br>{{ service.details }} <br><span class="idgrey">{{ service.$id }}</span></li>
        </ul>
        <h3>List All Categories</h3>
        <li ng-repeat="cat in servicecat">{{ cat.name }}</li>
      </div>            
      <div class="mdl-cell mdl-cell--3-col">
        <h3>Post Full Service Selected </h3>
        <p>
          <strong>{{ selectedService.title }}</strong><br>
          {{ selectedService.details }}
          <h5>Id of Selected Categories</h5>
          <ul>
            <li ng-repeat="cat in selectedCategoriesId">{{ cat.$id }}</li>
          </ul>
          <hr>
          <h5>Name of Selected Categories</h5>
          <ul>
            <li ng-repat="cat in selectedSrvCat">ID : {{ findcatscope.name }}</li>
          </ul>     
      </div>
    </div>
</div>
C. Banzet
  • 409
  • 2
  • 5
  • 19
  • 1
    Early versions of AngularJS used to "unwrap" promises in Angular Expressions. That is no longer the case. Avoid doing asynchronous operations in Angular Expressions. Instead chain those operations from the operation which created `servicecat`. – georgeawg Feb 19 '17 at 06:28
  • thanks @georgeawg for your answer. I'm not sure I really get your advice "chain those operations from the operation which created servicecat". I was just trying to get the list of the categories (servicecat) linked to a service with the "denormalize way" – C. Banzet Feb 20 '17 at 16:41
  • It seems hard to do with 2 variables in the ref : fb.child('servicecat/'+catid+'/services/'+srvid); or fb.child('services/'+srvid+'/servicecat/'+catid); – C. Banzet Feb 20 '17 at 17:01
  • Show the controller code that fetches `$scope.servicecat`. – georgeawg Feb 20 '17 at 19:55
  • This looks like an [**XY Problem**](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem#). Using `ng-if` directive will not solve the problem of filtering one set of data from an asynchronous API call with the results from another asynchronous API call. Functions invoked with `ng-if` need to be synchronous and [idempotent](http://www.restapitutorial.com/lessons/idempotency.html) and Firebase accesses are asynchronous. Look into [chaining promises](https://docs.angularjs.org/api/ng/service/$q#chaining-promises) to solve the problem. – georgeawg Feb 20 '17 at 21:39
  • I understand my XY problem comes from my confusion between synchronous and asynchronous ways. Thanks for your input on this issue. So from what I understand there is no use of ng-if directive with Firebase as Firebase is asynchronous. Now trying to solve the problem of making an array with two tables with chaining promises... – C. Banzet Feb 20 '17 at 22:41
  • I recommend writing a new question which has a [Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve) of what you want to accomplish with two sequential calls to the firebase server. – georgeawg Feb 20 '17 at 23:26
  • http://stackoverflow.com/questions/42356822/cant-create-with-firebase-and-angularjs-a-firebasearray-with-two-sequential-cal – C. Banzet Feb 21 '17 at 15:00

0 Answers0