5

I'm dynamically creating inputs with *ngFor. So, I would like to set focus on the last created input. Could someone help me?

This is my template code:

                <div class="col-6">
              <mat-card class="dataItens">
                <mat-card-header>
                  <mat-card-title>Itens de dados</mat-card-title>
                </mat-card-header>
                <mat-card-content>
                  <section *ngFor="let dataItem of elementaryProcess.dataItens; let i = index;">
                    <mat-form-field class="input-dataItens">
                      <input matInput [(ngModel)]="dataItem.name" name="dataItem{{ i }}" id="dataItem{{ i }}"
                             autocomplete="off"
                             [ngModelOptions]="{standalone: true}" class="input-dataItens"
                             (keyup.arrowDown)="dataItemOnKeyUp()"/>
                    </mat-form-field>
                    <button type="button" mat-icon-button (click)="removeDataItem(i)">
                      <mat-icon>delete</mat-icon>
                    </button>
                  </section>
                </mat-card-content>
                <mat-card-actions>
                  <form>
                    <!-- Add button -->
                    <button mat-stroked-button color="primary" type="button" (click)="addDataItem()">Adicionar
                    </button>
                    <!-- ngFor code -->
                  </form>
                </mat-card-actions>
              </mat-card>
            </div>

And this is the function I use to add the inputs:

  addDataItem() {
    this.elementaryProcess.dataItens.push(new DataItem());
  }
0

2 Answers 2

6

Using ViewChildren and ViewChildren.changes. See this SO

I updated the stackblitz to focus using arrows keys. It's so simple than create a function

@ViewChildren('input') inputs: QueryList<ElementRef> //<--here declare our "inputs"
focusElement(index:number){
    const input=this.inputs.find((x,i)=>i==index)
    if (input)
      input.nativeElement.focus()
  }

And in the .html we ise keydown.arrowUp and keydown.arrowDown

<p *ngFor="let el of elements;let i=index">
  <input #input (keydown.arrowUp)="focusElement(i-1)"
                (keydown.arrowDown)="focusElement(i+1)" >
</p>

Updated, as Victor comments below, there's a problem when you has no items. It's because I forget check if there are inputs. So, when subscribe to inputs.changes we need check it

this.inputs.changes.pipe(takeWhile(()=>this.alive)).subscribe(() => {
  if (this.inputs.length)  //<--add this "if"
    this.inputs.last.nativeElement.focus()
})
Sign up to request clarification or add additional context in comments.

4 Comments

It is the solution I adopted, but It partially works. When I delete all components and insert the first one again, it stop to focus. Here, an example: stackblitz.com/edit/set-focus-example
@VictorVidigalRibeiro, I updated the answer, the problem was that I did'nt check if there are elements, so, if there are no elements, Angular give an error and the app crash, Thanks for the advertistment!
I don't know where the "updated" code should be placed. Could you create a example or rework the code to have it clear, please?
if you see the stackblitz stackblitz.com/edit/…, in ngAferViewInit is where I subscribe to "this.inputs.changes", the "updated code" is that I added if (this.inputs.length)
1

There are multiple ways to do it but this code is just using template

U can do it with ngFor last like this

<ng-container *ngFor="let item of [1,2,3]; last as isLast" >

<input *ngIf="!isLast"type="text">

<ng-container *ngIf="isLast" >
   <input type="text" #lastOne>
   {{lastOne.focus()}}
</ng-container>
</ng-container>

another way to do it like this.

<ng-container *ngFor="let item of [1,2,3]" >

<input  type="text" #lastOne>

{{lastOne.focus()}}
</ng-container>

feel free to choose

enter image description here enter image description here https://stackblitz.com/edit/angular-7-master-g3qsmt?file=src%2Fapp%2Fapp.component.html

8 Comments

Use interpolation to execute a function is a brilliant work-around, but I think that there're a 'more canonical way' (see my comment)
Yes this is first thing that came into my mind. but after some search I found this one.
In the first solution I should duplicate the <input> element. My <input> is very big: <input #dataItemInput matInput [(ngModel)]="dataItem.name" name="dataItem{{ i }}" id="dataItem{{ i }}" autocomplete="off" [ngModelOptions]="{standalone: true}" class="input-dataItens" (keydown.arrowUp)="focusDataItemInput(i-1)" (keydown.arrowDown)="focusDataItemInput(i+1)" (keyup.enter)="dataItemOnKeyUp()" (keyup.delete)="removeDataItem(i)"/>
The second solution does not work. It hold the focus on all inserted input.
may be it depends upon angular version bcs it was working when i posted
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.