0

Within a component of my Angular application, I've subscribed to an Observable that returns a json array from a function in one of my services. That response is getting returned to the component correctly.

In .subscribe I am mapping fields from each JSON object into properties of a new class instance (object) using nested for loops and then adding/pushing each instance/object into an array of that class type.

When I view the results in the console in Chrome coming from console.log(this.contractsList), all fields are getting populated correctly with the exception of one which shows as:

listOffices: Array(0)

This string array should always have either 1 or 2 elements.

I realize I should probably be performing this deserialization/mapping within my service rather than the component however I dont think that is resulting in this issue. I plan on refactoring once I get it working. Rather, I think it relates to Closure which still confuses me quite a bit. I assume so because when I try console.log(officeval) anytime before I try to assign its value to the object, I see the correct values in the console.

Here is the method in my component that calls subscribe:

getContracts() {

  this.contractService.getContracts().subscribe(
    (res) => {

        let officeval = new Array<string>();

        for (var x = 0; x < res.length; x++) {
          for (var z = 0; z < res[x].length; z++) {
             if (res[x][z].resources.office !== undefined){
               for (var a = 0; a < res[x][z].resources.office.values.length; a++) {
                 officeval.push(res[x][z].resources.office.values[a]);
               }
             }
            else {
                  officeval.push("Not a Hive Policy");
                 }

            this.createContract(res[x][z].id, res[x][z].name,"placeholder for type",res[x][z].isEnabled, res[x][z].service,
            officeval, ["q", "w"], ["q", "w"],["q", "w"], ["q", "w"],["q","r"]);

          } console.log(this.contractsList);       
    }
  },
    (err) => console.log(err))
  console.log("logging contracts");
  }

  createContract(id:number, contractName:string, type:string, isEnabled:boolean,
     service:string, listOffices:string[], listRooms:string[],
     listSeats:string[], contractItemsEntries:string[], contractItemsOwners:string[], contractItemsSessions:string[]) {

    this.contractsList.push (new Contract (id, contractName, type, isEnabled,
     service, listOffices, listRooms,
    listSeats, contractItemsEntries, contractItemsOwners, contractItemsSessions ));

  }
}

Here is an example of the output in Chrome console:

[Contract, Contract, Contract...] x6 
-opened Contract Array:
0:Contact
1:Contract
2:Contract
...
-opened Contract:
listOffices: Array(0) -- ???
listRooms: Array(2) --contain correct values
listSeats: Array(2) --contain correct values
id:1
isEnabled: true
service: contractService
type: "placeholder for type"
HendPro12
  • 1,094
  • 3
  • 17
  • 50

3 Answers3

0

Since we don't have a clue about you res object, are you sure that

res[x][z].resources.office.values.length > 0

It's the only case in which your officeVal isn't filled. (Cause it don't loop obviously)

So I'll bet on that.

Noki
  • 383
  • 1
  • 9
  • Im positive that it always is greater than 0 when res[x][z].resources.office !== undefined. As mentioned, if I place a console.log(officeval) immediately after else{officeval.push("Not a Hive Policy") the console lists values for each iteration of the loop. – HendPro12 May 19 '17 at 21:04
  • Okay, so your object is created. exists and fill. Maybe the conversion between Array and string[] but that seems weird – Noki May 19 '17 at 21:40
  • so strange thing is if I change a < res[x][z].resources.office.values.length to a < res[x][z].resources.office.values.length -1 the values get populated but then I encounter what amrinea mentioned regarding 93+ values in each object's array rather than 1 or 2. – HendPro12 May 19 '17 at 22:12
0

From looking at the code, office.values must have a length of 0, which means nothing is getting added to it and is therefore an Array(0). This can be confirmed if you can post the JSON coming back from getContracts.

Also, once you get it putting items into the officeval Array, you may notice that it has more items than it should because it doesn't appear your clearing out officeval in each iteration of your loop. This may be intentional, but my guess is that it's not.

amrinea
  • 553
  • 4
  • 12
  • yes, Ive already thought that officeval will need to be reset using officeval.length=0 and have been trying to determine when to make that happen. However, Ive pushed solving that to the side until I actually get values populated. – HendPro12 May 19 '17 at 20:58
  • also if I put console.log(res[x][z].resources.office.values.length) immediately before the for loop which uses it as a counter, all values returned and shown in the console are 1 or greater – HendPro12 May 19 '17 at 21:07
  • Have you tried debugging the code through the console and putting a breakpoint on both `officeval.push` lines? – amrinea May 20 '17 at 00:49
  • I moved the declaration of officeval outside of the getContracts() function so that I can refer to it inside of createContract() with this.officeval. Ive been stepping through the debugger and see the values getting set when each Contract is created and pushed to the contractsList array. The reason the listOffices array is appearing as length 0 in each contract at the end is because after pushing a contract to the contractsList I call this.officeval.length = 0 in order to reset it. The reset is resetting the value in each contract element in the contractsList. – HendPro12 May 20 '17 at 10:12
0

The root cause of the issue was related to:

  1. Not realizing that javascript passes complex data types by reference (see this article) (these posts were my guide in how to accomplish the pass by value rather than by reference: one, two)
  2. Not resetting the officeval array back to 0 after each inner for loop iteration

To fix, I:

  1. Moved the declaration of the officeval array outside of my outer function so that it could be referenced in my inner function using the this keyword
  2. Used the slice method to create a by value copy of officeval which I then pass into my inner function for use in creating new contracts and adding them to the contractsList array.
  3. Reset the value of officeval immediately after each time I push a new contract to the contractsList.

Here is the final version of my code:

officeval: string[] = []

getContracts() {

  this.contractService.getContracts().subscribe(
    (res) => {

        //let officeval = new Array<string>();

        for (var x = 0; x < res.length; x++) {
          for (var z = 0; z < res[x].length; z++) {
             if (res[x][z].resources.office !== undefined){
               for (var a = 0; a < res[x][z].resources.office.values.length; a++) {
                 officeval.push(res[x][z].resources.office.values[a]);
               }
             }
            else {
                  officeval.push("Not a Hive Policy");
                 }

            var testArray = this.officeval.slice(0);

            this.createContract(res[x][z].id, res[x][z].name,"placeholder for type",res[x][z].isEnabled, res[x][z].service,
            testArray, ["q", "w"], ["q", "w"],["q", "w"], ["q", "w"],["q","r"]);

          } console.log(this.contractsList);       
    }
  },
    (err) => console.log(err))
  console.log("logging contracts");
  }

  createContract(id:number, contractName:string, type:string, isEnabled:boolean,
     service:string, listOffices:string[], listRooms:string[],
     listSeats:string[], contractItemsEntries:string[], contractItemsOwners:string[], contractItemsSessions:string[]) {

    this.contractsList.push (new Contract (id, contractName, type, isEnabled,
     service, listOffices, listRooms,
    listSeats, contractItemsEntries, contractItemsOwners, contractItemsSessions ));

    this.offiveval.length = 0;

  }
}
Community
  • 1
  • 1
HendPro12
  • 1,094
  • 3
  • 17
  • 50