0

I've got a User entity in my system. Each user has a mail email (not relevant here) and an alternateEmails property which is an array of strings (to have more multiple less important emails).

I need to create a form that will have the main email displayed always - and will have all alternate emails - 1 input for each alternate email. And a button add email. Pretty standard interface thing in UI.

This is what I have now:

user-form.component.html:

<div class="form-group" *ngFor="let ae of user.alternateEmails; let i = index">
    <label for="alternateEmail{{i}}">alternate email {{i}}:</label>
    <input class="form-control" name="alternateEmail{{i}}" id="alternateEmail{{i}}" placeholder="alternate email" type="text" [(ngModel)]="user.alternateEmails[i]" />
</div>

<button (click)="addAlternateEmailClicked()">add email</button> 

user-form.component.ts:

addAlternateEmailClicked(){
  this.user.alternateEmails.push('');
}

It displays data very well. When I load data from the server, it reflects the number of alternate emails the user has (displays none if there were none, displays 2 if there were 2, etc):

enter image description here

When I click add email, new input appears:

enter image description here

The crazy problem I'm facing is that when I focus on a given alternate email (click inside the input, so that I can start writing), I can type only one char - and the input loses focus. I get no idea why is focus/blur working behind the scenes. Perhaps it has something to do with angular magic.

I'd appreciate (1) how to change the code to make it usable :) (no loosing focus on dynamic inputs) and (2) explanation on why is current solution wrong.

ducin
  • 25,621
  • 41
  • 157
  • 256
  • [**Refer this answer**](https://stackoverflow.com/questions/44224772/why-output-not-working-in-angular-2) which has a demo. – Aravind May 28 '17 at 13:15
  • @Aravind this exact demo is unrelated to dynamic input insertion. What do you mean? – ducin May 28 '17 at 13:17
  • are you sure that is diffferent from your expectation – Aravind May 28 '17 at 13:18
  • @Aravind look at the screenshots. You've got just items displayed, I've got inputs to receive user input. Can you see a difference? – ducin May 28 '17 at 13:20
  • If I were you I'd switch to FormBuilder and use a FormArray instead, a normal form doesn't really support array-like data like this is. Would probably solve the issue too. – Chrillewoodz May 28 '17 at 13:57
  • Have you tried using [(ngModel)]="ae" instead of [(ngModel)]="user.alternateEmails[i]" ? – Ori Price May 28 '17 at 14:11
  • Maybe a plunkr would help for debugging. – Chrillewoodz May 28 '17 at 15:43
  • 1
    I may be late, but all you need is a custom `trackBy` function. Put a `trackBy: myCustomTB` in your *ngFor, and in your component, add a function with two optional parameters (obviously called MyCustomTB), and that returns the first one. You the' should be good to go. –  May 28 '17 at 19:31

1 Answers1

0

You could try this solution instead:

constructor(private fb: FormBuilder) {}

public userForm: FormGroup = this.fb.group({
  emails: this.fb.array([])
});

Swap ngModel for formControlName:

<div class="form-group" *ngFor="let email of userForm.emails.controls; let i = index">
    <label [for]="i">Alternative email {{i}}:</label>
    <input class="form-control" [id]="i" placeholder="Alternative email" type="text" formControlName="email">
</div>

<button (click)="addAlternateEmailClicked()">add email</button> 

Push a new control to the form array:

addAlternateEmailClicked(){

  const emails: FormArray = <FormArray>this.userForm.get('emails');

  // Validators is optional
  emails.push(this.fb.control('', Validators.required));
}
Chrillewoodz
  • 27,055
  • 21
  • 92
  • 175
  • I guess that heavy angular built0in solutions can manage this out of the box. I was hoping that there might be a more light-weight approach to deal with it. Especially that, in my scenario, everything works fine - only except for this focus/blur... – ducin May 28 '17 at 15:38
  • @ducin I think the issue is that you're trying to bind to a dynamic `ngModel`, which doesn't really work all that well unless you're doing it with this approach. It could feed some unexpected bugs sometimes. I think that if you try FormBuilder you'll never go back, it's really neat and powerful. – Chrillewoodz May 28 '17 at 15:41