1

I have a component that is subscribed to some data used to populate a table. This table uses *ngFor to loop over the array of data and output it to the page, typical stuff.

When I define my array like so importResults: ImportResults[];, my data appears to get stored as intended and I am left with an array of objects.

ngOnInit() {
    // Subscribe to our subject that lets us know about the added employees
    this._massEmpService.importedData.subscribe(obj => {
        if (obj) {
            obj.checked = false;
            this.importResults = obj;
        }
    });
}

enter image description here

With this setup, I can use *ngFor without issues by simply doing:

<tbody>
    <tr *ngFor="let i of importResults" >
        <td>
            <input type="checkbox"
                    id="checkbox_{{ i.QID }}"
                    [checked]="i.checked"
                    (click)="toggleSelectedEmployee(i)"
                    [(ngModel)]="i.checked" />
        </td>
        ...
    </tr>
</tbody>

However... when I initialize my array like so importResults: ImportResults[] = []; it alters my data structure.

This leaves me with an array of arrays of objects?

enter image description here

This causes me to have to do some weird nested looping on my table which doesn't seem right.

<tbody *ngFor="let res of importResults">
    <tr *ngFor="let i of res" >
        <td>
            <input type="checkbox"
                    id="checkbox_{{ i.QID }}"
                    [checked]="i.checked"
                    (click)="toggleSelectedEmployee(i)"
                    [(ngModel)]="i.checked" />
        </td>
        ...
    </tr>
</tbody>

Is this expected behavior to need to nest stuff like this? The first way works fine for how I would expect to be able to loop but because its not initialized, I can't push new data to it which is why I had to go with the expected way of defining it Array[] = [];

Am I doing something wrong here?

11
  • 1
    Without digging deep into it, these sort of JS questions always mean, "somehow I messed up the scope". Are you certain that, every iteration, you are actually creating/returning a new array, or do you continuously modify the old one? And/or, remember, even if the array is new, the references to the objects they hold may not be. So if you act on a "new" array with references to the "old" objects, same effect...you mess with the old(s) array. These days, as a general programming guideline, I would tend to say, "copy everything...figure out optimization later." Commented Jul 15, 2017 at 1:53
  • 1
    Somehow that sound wonky. But I'm tired and want to play guitar. I bookmarked this and will address in the morning unless you get an answer. Commented Jul 15, 2017 at 1:57
  • 2
    @SBB you just explained your own bug: using push is wrong. Use = instead. Using push results in a nested array. Commented Jul 15, 2017 at 2:09
  • 2
    @SBB, from the code and comments, it looks like obj is an array, so when you are initializing importResults: ImportResults[] = []; and doing this.importResults.push(obj);, you are pushing the array obj inside an empty array of []. That's why it gets messed up. Instead if you do this.importResults = obj, you won't have the problem. The other way would be to take each object from obj and push them in importResults instead of pushing the whole obj array. Commented Jul 15, 2017 at 2:11
  • 1
    @AluanHaddad - I am just as confused as you are :) User picks item, clicks button. This item has .next called on it and sent through the behavior subject which this current component is subscribed to. Data is then rendered to the page via ngFor and each item in the cart has a checkbox. I can check all three items in the cart. Now, if I add another item and click the button, this process repeats. By using importResults = obj I am replacing the whole shopping cart array so now all those checked boxes are lost. If i push it, the items already in the cart remain untouched. Commented Jul 15, 2017 at 2:33

1 Answer 1

1

In your first instance, you declare the variable, but don't initialize it, then when you subscribe to the data service you assign the resulting array of objects to the variable:

 this.importResults = obj;

In your second case, you declare the variable and initialize it as an empty array:

importResults: ImportResults[] = []; 

Then when you get your data back from the service you're doing this:

this.importResults.push(obj);.

This is taking the returned array of objects from the data service and pushing it into the array you've already created, so that's why it ends up being nested. In the first, you're making your variable equal to the array of objects you're getting back, and in the second you're pushing the array of objects into the existing array.

If you used the same assignment in the second case:

 this.importResults = obj;

You wouldn't have this problem, whether you initialized the variable to an empty array when you declared it or not.

If what you're trying to do is fetch one or more objects from a service and add them to an existing array, which may already have one or more objects inside, then you want to return the objects via subscribe, and iterate over them, pushing each object in that returned array one at a time into the existing array.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks - this suggestion was made by others as well but I have convinced myself that I would be better off using .push(...obj) instead. However, others said that may be incorrect. While its working for me, I fear now that I have done something wrong elsewhere if this approach was incorrect. Since I want to be able to push new data into this array when I get it and not overwrite it, is pushing more appropriate than setting it?
In the case of a shopping cart, you would start by initializing your cart as an empty array (empty cart), then you would push new items into that cart array one at a time when users click 'add' on items. If you saved that entire cart to a database, and then restored it for them when they came back later, you would fetch the entire cart, which would be an array of objects, and you would assign that fetched array of objects to the cart variable, effectively overwriting whatever was there (probably empty) with their restored cart, and then push additional items as they continued shopping.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.