1

I am working on an app where I am pulling data from our private git enterprise server. I have an array of strings for the repos to check, I take that data and combine it with some from a local JSON file to give me a structure like:

{ 
  repo1: {},
  repo2: {},
  repo3: {}
}

The object comes back exactly like I expect. The issue is when I want to get a count. Generally in JS I use Object.keys, but when I do it returns an empty array. I got around this temporarily by creating a local array and pushing the names into it so I could iterate with ngFor. The more I dig in, the less sense it makes. I can use Object.keys on an object literal, or my predefined objects with no problem. The problem is when I try to use it on this returned object. I am running the code to get the object in ngOnInit(). When I console.log the object it comes back exactly as expected, but if I change the log to be Object.keys(object) it returns blank. I thought perhaps it was a timing issue so I wrapped the function in a promise and chained a .then off of it. Again, the object returned by the promise looks correct, but I cannot use Object.keys on it. I also tried returning Object.keys(object) instead of the object in the function but it still came back blank.

Is there something I am missing? Is there a more 'Angular' way to handle this? I've been using Angular for a while but never run into something like this. Wondering if it's a timing issue or something.

tabContentSetup(tabArray) {
const filteredObject = {};
const filteredArray = [];
// For each element in the array, get the repo name
tabArray.forEach((repo) => {
  // Grab all prs for the current repo
  this.gitApiService.getPrs(repo)
    // Subscribe to observable which returns an array of PR objects from the repo
    .subscribe((prObjectArray) => {
      // Iterate over each prObject in prObjectArray
      prObjectArray.forEach((prObject) => {
        // Grab the github user that submitted PR in lowercase
        const prObjectUser = prObject.user.login.toLowerCase();
        // If this PR belongs to a consultant, disregard and return
        if (this.instructorsArray.includes(prObjectUser)) {
          return;
        }
        // If filteredObject does not have the current repo yet, add it as a new object
        if (!filteredObject[repo]) {
          filteredObject[repo] = {
            count: 1,
            cohortsArray: [],  // TODO fix this with Object.keys
            cohorts: {}
          };
          filteredArray.push(repo); // TODO find out why Object.keys isn't working
        } else {
          filteredObject[repo]['count']++; // TODO could be shortened with Object.keys
        }
        if (this.gitHubDict[prObjectUser] === undefined) {
          console.warn('Cannot find: ' + prObjectUser + ' in Cohort JSON files');
          return;
        }
        const assignedCohort = this.gitHubDict[prObjectUser].cohort;
        if (!filteredObject[repo]['cohorts'][assignedCohort]) {
          filteredObject[repo].cohortsArray.push(assignedCohort);
          filteredObject[repo]['cohorts'][assignedCohort] = {
            count: 1,
            prs: []
          };
        } else {
          filteredObject[repo]['cohorts'][assignedCohort].count++;
        }
        filteredObject[repo]['cohorts'][assignedCohort]['prs'].push({
          name: this.gitHubDict[prObjectUser].first + ' ' + this.gitHubDict[prObjectUser].last,
          cohort: assignedCohort,
          git: prObjectUser,
          url: prObject.html_url,
          created_at: prObject.created_at,
          updated_at: prObject.updated_at
        });
        });
    });
});
return Object.keys(filteredObject);
}

It's way too long which is why I'm going back and trying to refactor with Object.keys.

Rudenate3
  • 1,821
  • 2
  • 12
  • 13

1 Answers1

0

The return does not wait on the subscriber, so filteredObject is empty. You should make filteredObject an Observable (or rather, a Subject) and subscribe to it. This part of your code is executed sequentially on the method call:

const filteredObject = {};
const filteredArray = [];
tabArray.forEach((repo) => {
    this.gitApiService.getPrs(repo).subscribe();
}
return Object.keys(filteredObject);

the part within the subscribe() callback method is executed, when getPrs() triggers the next() in the returned observable (I guess you return HttpClient.get()?). So, on call of tabContentSetup(), it will return an empty object, because the HTTP requests are not done yet.

I suggest using a BehaviorSubject (see this answer: BehaviorSubject vs Observable?). Declare the filteredObject outside of your function and use it's built in getter .value and "setter" .next():

// initialize a Subject with an empty object
filteredObjectObservable = new BehaviorSubject({});
// initialize a public variable for the count
repoCount: number;

tabContentSetup(tabArray) {
    tabArray.forEach((repo) => {
        this.gitApiService.getPrs(repo).subscribe((prObjectArray) => {
              // set the variable here and use the built in getter, to get the current value
              let filteredObject = this.filteredObjectObservable.value;
              // do your stuff

              // at the very end of the subscribe callback, set the new value. This will also trigger all subscribers
              this.filteredObjectObservable.next(filteredObject);
        }
    }
    // subscribe to the BehaviorSubject (this could also be in the component's OnInit method or anywhere else).
    this.filteredObjectObservable.subscribe((value) => {
        // triggers each time the Subject's next() method is called
        this.repoCount = Object.keys(value).length;
    }
}

Note: Code is untested :-)

masterfloda
  • 2,908
  • 1
  • 16
  • 27