2

This question is not solved by answers like this one despite a similar wording of the problem.

I'm using a custom component and the following work as supposed to. No errors, no warnings.

<app-setter [(setting)]="records[0]"></app-setter>
<app-setter [(setting)]="records[1]"></app-setter>
<app-setter [(setting)]="records[2]"></app-setter>

Naturally, I prefer to use a template generator like this.

<app-setter *ngFor="let record of records;"
            [(setting)]="record"></app-setter>

This produces the following error.

Cannot assign to a reference or variable!

Googling leads to a lot of hits on the same theme - there's a collision between a variable in the component and the ID of the generated tags. It's not the case here, as far I can see. I renamed the only field in my component to records and I don't even see any IDs being assigned to the generated tags.

What can be the cause of the issue and how can I diagnoze it further? I'm not sure how to learn the actual place where that occurs neither (it's compiler.js:xxxx, which tells me rather nothing).

edit

I've tried the following without success.

<ng-container *ngFor="let record of records;">
  <app-setter [(setting)]="record"></app-setter>
</ng-container>

However, I noticed that removing the bananas from the box-of-bananas makes the error vanish (of course with the result that the two-way binding is broken. This works:

<ng-container *ngFor="let record of records;">
  <app-setter [setting]="record"></app-setter>
</ng-container>

It tells me rather little. But it tells something, right?

The component code looks like this.

export class AssessmentComponent implements OnInit {
  constructor() { }
  records: Record[];
  ngOnInit() { this.records = [new Record("x", 3), new Record("y", 2)]; }
  onAddition(event: Record) { console.log(); }
}
DonkeyBanana
  • 3,266
  • 4
  • 26
  • 65
  • Please provide a stackblitz of your issue, the code we are seeing should work. – AT82 Aug 20 '19 at 16:34
  • If your settings is an object you needn't use `@Output` -any change in a propertie of the object is allowed-, but the code must be work – Eliseo Aug 20 '19 at 16:43
  • @AJT_82 No, it shouldn't, apparently. I'm a bit surprised myself but apparently, one has to refer by index to the original array and not the individual item. At least, that works and this doesn't. Check the answer below. I'd +1 it for effort and extra insight too. Also, feel free to pitch in on **why** we need to do that, if you've got an idea. – DonkeyBanana Aug 20 '19 at 16:44
  • There is nothing wrong with your code, should work just fine :) https://stackblitz.com/edit/angular-dk18m6?file=src/app/hello.component.ts – AT82 Aug 20 '19 at 16:46
  • 1
    That is why I asked for the demo, there must be something we are not seeing, because you **should not** need to do it like in either of the answers ;) There is absolutely nothing wrong with the code we are seeing. – AT82 Aug 20 '19 at 16:51
  • @AJT_82 I stand corrected in face of undeniable evidence, mate. The only difference I noticed is that Blitz runt Ang7 and I have Ang6 but that's hardly the reason. Let me poke around when I'm less stressed and reproduce the issue. You're obviously right. I'd like to show the whole project of mine and cut off stuff gradually. Is there a way to upload the whole shabang to Blitzy? Or do I have to copy file by file? – DonkeyBanana Aug 20 '19 at 17:01
  • I did some digging since I got really curious. This error does occur in Angular v6. So the accepted answer is correct in this case. – AT82 Aug 20 '19 at 18:37
  • 1
    @AJT_82 Nice, so it wasn't me going insane, haha. – DonkeyBanana Aug 21 '19 at 18:48

2 Answers2

2

EDIT: Credits to @thenolin for figuring it out. But from what OP has provided, I think we should use records[i] instead of record[i].

<app-setter *ngFor="let record of records; let i = index" [(setting)]="records[i]">
</app-setter>

Also, no need to use <ng-container>.

Original answer:

<ng-container *ngFor="let record of records;">
   <app-setter [(setting)]="record"></app-setter>
</ng-container>

ng-container is a grouping element that doesn't interfere with styles or layout.

You can replace <ng-container> in above snippet with <div> or any other element but it might affect your layout.

Nikhil
  • 6,493
  • 10
  • 31
  • 68
  • Are you sure *ng-container* is an Angular directive? It looks suspiciously like AngularJS thing... – DonkeyBanana Aug 20 '19 at 16:14
  • No, it is in Angular too - https://angular.io/guide/structural-directives#ng-container-to-the-rescue. – Nikhil Aug 20 '19 at 16:15
  • this is the best solution if you ever need to use the *ngFor and *ngIf directives on one element.. since you can't use them both in the same element this method is best – thenolin Aug 20 '19 at 16:17
  • I just tried it and , regrettably, the result is the same. I tried with DIV too. I've never used a surrounding parent before because the issue is rather new to me. Can you explain some more? – DonkeyBanana Aug 20 '19 at 16:19
  • @thenolin Regrettably, it's not a solution in my case. I still get the same error. Also, please note that I'm not using *ngIf* in my code at the moment. The collision is due to something else. – DonkeyBanana Aug 20 '19 at 16:20
  • @DonkeyBanana Can you please add your component code to the question? – Nikhil Aug 20 '19 at 16:23
  • @Nikhil Gladly. Check the edit I made now and the rest is coming up in a jiffy. – DonkeyBanana Aug 20 '19 at 16:26
  • @DonkeyBanana I think the error has something to do with two-way data binding. I'm trying to replicate the scenario to find the actual cause of the error. Is it working as expected when you remove the parenthesis? – Nikhil Aug 20 '19 at 16:34
  • Thanks mate. @thenolin already broke the mystery. Apparently we have to refer to the original array and pick from by index. Oh, that's so not C#'ish *foreach*... – DonkeyBanana Aug 20 '19 at 16:42
  • Cool. I'm glad it worked. I'm still trying to understand why my approach didn't work. – Nikhil Aug 20 '19 at 17:23
  • Your solution would work if you add an index to the *ngFor loop.. the object/array that DonkeyBanana wanted to set was an array, hence when the loop would execute the array would also need to iterate – thenolin Aug 20 '19 at 17:51
  • 1
    @thenolin - I think index is unnecessary. By doing record[i] as described in your answer, we are essentially using records[0][0], records[1][1], records[2][2]. Not sure why this worked for OP. – Nikhil Aug 20 '19 at 18:02
  • After thinking about it you're right. I'm under the impression that the index is needed since the OP is doing two way binding. If he were just setting the property then it wouldn't be needed, but since he's also getting it back, it's being used to correlate the arrays (I think).. please correct me if I'm wrong – thenolin Aug 20 '19 at 18:05
  • 1
    @thenolin - In such case, we should just use records[i], instead of record[i]. I think we don't have enough information to completely understand the issue. You are right about the index: https://stackoverflow.com/a/47567879/2924577. – Nikhil Aug 20 '19 at 18:56
  • 1
    @Nikhil Yes, it needs to be *record **S** [i]* - probably just a typo. – DonkeyBanana Aug 21 '19 at 18:47
2

You need to set an index with the *ngFor loop like this.

 <app-setter *ngFor="let record of records; let i = index"
                [(setting)]="records[i]">
 </app-setter>

Like I was alluding to in my comment in the other solution, Nikhil's suggestion about using a ng-container is good practice. While you aren't doing it here, if you ever need an *ngIf & *ngFor on the same element, use the ng-container like described in the other answer and it will produce the desired effect.

DonkeyBanana
  • 3,266
  • 4
  • 26
  • 65
thenolin
  • 1,013
  • 3
  • 10
  • 28
  • 1
    Two things. (1) great suggestion on style, which I agree with but not answer to my question. And (2), oops, it **was** answer to my question indeed! (I'm baking in a trail of thoughts over a certain time into one comment here.) It's definitely greeny-checky as the problem is resolved. Would you be tempted by a +1 to share some light over why simple *record* won't do and that we have to use the indexing from the array? Or would that require +50 of a bounty? (I'm stingy AF with my rep so 50 is a huge honor, you know...) – DonkeyBanana Aug 20 '19 at 16:40
  • No need for a bounty, happy I can help. My understanding is that since your using two way binding, you need to implicitly provide the index for correlating the correct 'record' object with the 'setting' object in each array respectively. Since you don't define an individual html property for each record, it needs a unique property to bind correctly. (This is my understanding.. please correct me if I'm understanding this wrong) – thenolin Aug 20 '19 at 18:03