Angular2 - Fokussieren eines Textfelds auf das Laden von Komponenten

79

Ich entwickle eine Komponente in Angular2 (Beta 8). Die Komponente verfügt über ein Textfeld und eine Dropdown-Liste. Ich möchte den Fokus im Textfeld setzen, sobald die Komponente geladen wird oder wenn sich das Dropdown-Ereignis ändert. Wie würde ich das in angle2 erreichen? Es folgt HTML für die Komponente.

<div>
    <form role="form" class="form-horizontal ">        
        <div [ngClass]="{showElement:IsEditMode, hidden:!IsEditMode}">
            <div class="form-group">
                <label class="control-label col-md-1 col-sm-1" for="name">Name</label>
                <div class="col-md-7 col-sm-7">
                    <input id="name" type="text" [(ngModel)]="person.Name" class="form-control" />

                </div>
                <div class="col-md-2 col-sm-2">
                    <input type="button" value="Add" (click)="AddPerson()" class="btn btn-primary" />
                </div>
            </div>
        </div>
        <div [ngClass]="{showElement:!IsEditMode, hidden:IsEditMode}">
            <div class="form-group">
                <label class="control-label col-md-1 col-sm-1" for="name">Person</label>
                <div class="col-md-7 col-sm-7">
                    <select [(ngModel)]="SelectedPerson.Id"  (change)="PersonSelected($event.target.value)" class="form-control">
                        <option *ngFor="#item of PeopleList" value="{{item.Id}}">{{item.Name}}</option>
                    </select>
                </div>
            </div>
        </div>        
    </form>
</div>

parag
quelle

Antworten:

78

Die Verwendung eines einfachen autofocusHTML5-Attributs funktioniert für das Szenario "on load"

 <input autofocus placeholder="enter text" [(ngModel)]="test">

oder

<button autofocus (click)="submit()">Submit</button>

http://www.w3schools.com/TAgs/att_input_autofocus.asp

Nikhil Nambiar
quelle
45
Dies scheint nur zu funktionieren, wenn Sie die Seite aktualisieren oder von Grund auf neu laden. Wenn Sie über einen Router zur Formularkomponente navigieren, wird das Feld nicht ausgewählt.
Dennis W
3
Dies ist keine gute Lösung, wenn wir mehrere Komponenten haben. Dies funktioniert nur für die erste Standardkomponente
Paresh Varde
+1 - Danke Nikhil, es ist perfekt; Ich benutze es so: <div class = "oder-name uk-tile-stummgeschaltet uk-text-left uk-display-block" * ngIf = "value! == cHttpUser"> {{value.name}} </ div> <input class = "uk-input uk-form-steuert-text oder -eingabe" * ngIf = "value === cHttpUser" [(ngModel)] = "value.name" autofocus (click) = "update ( ) "(keyup.enter) =" update () "(keyup.tab) =" update () "(blur) =" update () ">: Klicken Sie auf div. Musste erneut klicken, um einzusteigen und zu tippen, es war ein "halber Weg". Ein Klick nach links!
Olivier
77

Diese Antwort ist inspiriert von Post Angular 2: Fokus auf neu hinzugefügtes Eingabeelement

Schritte zum Festlegen des Fokus auf das HTML-Element in Angular2

  1. Importieren Sie ViewChildren in Ihre Komponente

    import { Input, Output, AfterContentInit, ContentChild,AfterViewInit, ViewChild, ViewChildren } from 'angular2/core';
    
  2. Deklarieren Sie den Namen der lokalen Vorlagenvariablen für das HTML, für das Sie den Fokus festlegen möchten

  3. Implementieren Sie die Funktion ngAfterViewInit () oder andere geeignete Lebenszyklus-Hooks
  4. Es folgt der Code, mit dem ich den Fokus eingestellt habe

    ngAfterViewInit() {vc.first.nativeElement.focus()}
    
  5. Fügen Sie #inputdem DOM-Element, auf das Sie zugreifen möchten, ein Attribut hinzu.

///This is typescript
import {Component, Input, Output, AfterContentInit, ContentChild,
  AfterViewChecked, AfterViewInit, ViewChild,ViewChildren} from 'angular2/core';

export class AppComponent implements AfterViewInit,AfterViewChecked { 
   @ViewChildren('input') vc;
  
   ngAfterViewInit() {            
        this.vc.first.nativeElement.focus();
    }
  
 }
<div>
    <form role="form" class="form-horizontal ">        
        <div [ngClass]="{showElement:IsEditMode, hidden:!IsEditMode}">
            <div class="form-group">
                <label class="control-label col-md-1 col-sm-1" for="name">Name</label>
                <div class="col-md-7 col-sm-7">
                    <input #input id="name" type="text" [(ngModel)]="person.Name" class="form-control" />

                </div>
                <div class="col-md-2 col-sm-2">
                    <input type="button" value="Add" (click)="AddPerson()" class="btn btn-primary" />
                </div>
            </div>
        </div>
        <div [ngClass]="{showElement:!IsEditMode, hidden:IsEditMode}">
            <div class="form-group">
                <label class="control-label col-md-1 col-sm-1" for="name">Person</label>
                <div class="col-md-7 col-sm-7">
                    <select [(ngModel)]="SelectedPerson.Id"  (change)="PersonSelected($event.target.value)" class="form-control">
                        <option *ngFor="#item of PeopleList" value="{{item.Id}}">{{item.Name}}</option>
                    </select>
                </div>
            </div>
        </div>        
    </form>
</div>

parag
quelle
2
Einfach genug, aber was passiert, wenn Sie den Fokus in einer anderen Komponente setzen müssen? Beispiel: Standardsuchfeld (in einer Kopfzeile) einer untergeordneten Komponente.
Rick Strahl
2
Wie wäre es mit einem dynamisch generierten Element?
Kaushik Thanki
Unter Verwendung von Winkelmaterial musste ich ChangeDetectorRefin den Konstruktor aufnehmen und dann this.cd.detectChanges();nach dem Fokusaufruf gemäß dieser Antwort von @jspassov aufrufen .
Derek Hill
30
<input id="name" type="text" #myInput />
{{ myInput.focus() }}

Dies ist der beste und einfachste Weg, da der Code "myInput.focus ()" nach der Erstellung der Eingabe ausgeführt wird

Sergey Gurin
quelle
3
Lol das funktioniert eigentlich! Ein bisschen hackig, aber sehr einfach und funktional, danke! :)
Wagner Danda da Silva Filho
5
Anfangs hat es mir sehr gut gefallen, aber es scheint, als würde es mich immer davon abhalten, andere Elemente auszuwählen (oder sogar zu kopierenden Text hervorzuheben). Musste hier mit Blick auf Kinder zur akzeptierten Antwort wechseln.
Jason W
4
Ja, diese Methode ist akzeptabel, wenn nur eine Bearbeitung auf dem Bildschirm angezeigt wird. Es verhindert, dass der Fokus auf andere Eingänge gelegt wird!
Sergey Gurin
1
Beachten Sie, dass die Methode myInput.focus () bei jedem Änderungszyklus ausgelöst wird. Dies kann zu unerwünschtem Verhalten führen, wie bei @JasonW angegeben.
Carsten
2
verstecktes Juwel !! :) Du rockst meinen Freund!
Manish Jain
29

In der ursprünglichen Frage wurde nach einer Möglichkeit gefragt, den Fokus zunächst oder später als Reaktion auf ein Ereignis festzulegen. Der richtige Weg, dies zu erreichen, scheint darin zu bestehen, eine Attributanweisung zu erstellen, die für jedes Eingabeelement festgelegt werden kann, und dann ein benutzerdefiniertes Ereignis zu verwenden, um die Fokusmethode für dieses Eingabeelement sicher auszulösen. Erstellen Sie dazu zunächst die Direktive:

import { Directive, Input, EventEmitter, ElementRef, Renderer, Inject } from '@angular/core';

@Directive({
    selector: '[focus]'
})
export class FocusDirective {
    @Input('focus') focusEvent: EventEmitter<boolean>;

    constructor(@Inject(ElementRef) private element: ElementRef, private renderer: Renderer) {
    }

    ngOnInit() {
        this.focusEvent.subscribe(event => {
            this.renderer.invokeElementMethod(this.element.nativeElement, 'focus', []);
        });
    }
}

Beachten Sie, dass renderer.invokeElementMethod für das nativeElement verwendet wird, das für Webworker sicher ist. Beachten Sie auch, dass focusEvent als Eingabe deklariert ist.

Fügen Sie dann die folgenden Deklarationen zur Angular 2-Komponente hinzu, die die Vorlage enthält, in der Sie die neue Direktive verwenden möchten, um den Fokus auf ein Eingabeelement zu setzen:

public focusSettingEventEmitter = new EventEmitter<boolean>();

ngAfterViewInit() { // ngOnInit is NOT the right lifecycle event for this.
    this.focusSettingEventEmitter.emit(true);
}
setFocus(): void {
  this.focusSettingEventEmitter.emit(true);
}

Vergessen Sie nicht, EventEmitter wie folgt über die Komponente zu importieren:

import { Component, EventEmitter } from '@angular/core';

Legen Sie in der Vorlage für diese Komponente das neue Attribut [focus] wie folgt fest:

<input id="name" type="text" name="name" 
    [(ngModel)]="person.Name" class="form-control"
    [focus]="focusSettingEventEmitter">

Importieren und deklarieren Sie schließlich in Ihrem Modul die neue Direktive wie folgt:

import { FocusDirective } from './focus.directive';

@NgModule({
    imports: [ BrowserModule, FormsModule ],
    declarations: [AppComponent, AnotherComponent, FocusDirective ],
    bootstrap: [ AppComponent ]
})

Um es noch einmal zusammenzufassen: Die Funktion ngAfterViewInit bewirkt, dass der neue EventEmitter ausgegeben wird. Da wir diesen Emitter dem Attribut [focus] im Eingabeelement in unserer Vorlage zugewiesen haben, haben wir diesen EventEmitter als Eingabe für die neue Direktive deklariert und den Fokus aufgerufen Methode in der Pfeilfunktion, die wir an das Abonnement für dieses Ereignis übergeben haben, erhält das Eingabeelement nach der Initialisierung der Komponente und bei jedem Aufruf von setFocus den Fokus.

Ich hatte das gleiche Bedürfnis in meiner eigenen App, und dies funktionierte wie angekündigt. Vielen Dank an: http://blog.thecodecampus.de/angular-2-set-focus-element/

Kent Weigel
quelle
Das ist großartig und es funktioniert, aber ich frage mich, ob es nur ein bisschen zu viel Arbeit für eine kleine Fokussierung ist. Ich wollte es als eine nette Sache für den Benutzer tun, da es keine Voraussetzung ist, aber es scheint eine übermäßige Menge an Code zu sein, um dem Benutzer einen einzigen Klick zu ersparen.
Ka Mok
3
@ KentWeigel - Dies ist eine wirklich klassische Lösung und funktioniert perfekt. Es ist die 'richtige' Antwort imo. Vielen Dank.
HockeyJ
Dies ist wirklich eine schöne Lösung, aber Sie haben die setFocusFunktion nicht verwendet
Hakan Fıstık
2

Ich hatte ein etwas anderes Problem. Ich habe mit Eingaben in einem Modal gearbeitet und es hat mich verrückt gemacht. Keine der vorgeschlagenen Lösungen hat bei mir funktioniert.

Bis ich dieses Problem gefunden habe: https://github.com/valor-software/ngx-bootstrap/issues/1597

Dieser gute Kerl gab mir den Hinweis, dass ngx-bootstrap modal eine Fokuskonfiguration hat. Wenn diese Konfiguration nicht auf false gesetzt ist, wird das Modal nach der Animation fokussiert und es gibt KEINE MÖGLICHKEIT, etwas anderes zu fokussieren.

Aktualisieren:

Fügen Sie dem modalen div das folgende Attribut hinzu, um diese Konfiguration festzulegen:

[config]="{focus: false}"

Update 2:

Um den Fokus auf das Eingabefeld zu erzwingen, habe ich eine Direktive geschrieben und den Fokus in jedem AfterViewChecked-Zyklus festgelegt, solange das Eingabefeld die Klasse ng-unberührt hat.

 ngAfterViewChecked() {
    // This dirty hack is needed to force focus on an input element of a modal.
    if (this.el.nativeElement.classList.contains('ng-untouched')) {
        this.renderer.invokeElementMethod(this.el.nativeElement, 'focus', []);
    }
}
FireGnome
quelle
Hallo @FireGnome, können Sie uns mitteilen, wo dies konfiguriert werden soll?
Cyberabis
Wie Sie hier sehen können: valor-software.com/ngx-bootstrap/#/modals#static gibt es ein Konfigurationsattribut [config] = "{Hintergrund: 'statisch'}" Sie können dort den Eigenschaftsfokus
festlegen
Hallo, ich habe das gleiche Problem. Ich habe das ngx-Modal und die Konfiguration hinzugefügt und versucht, den Fokus mit einer Direktive hinzuzufügen (noch kein Glück). Was haben Sie verwendet, um der Eingabe jedes Mal den Fokus hinzuzufügen? Danke
Simon
2

Es kann auch dynamisch so gemacht werden ...

<input [id]="input.id" [type]="input.type" [autofocus]="input.autofocus" />

Wo Eingabe ist

const input = {
  id: "my-input",
  type: "text",
  autofocus: true
};
SoEzPz
quelle
Das Gegenteil hat bei mir nicht funktioniert[autofocus]="false"
Teddybär
In Ihrem Fall wäre es Autofokus = "falsch"; Die [] sind nur für Auswertungen wie "myObj.key". Sie verwenden eine tatsächliche Zeichenfolge.
SoEzPz
2

Direktive für das erste Feld des Autofokus

import {
  Directive,
  ElementRef,
  AfterViewInit
} from "@angular/core";

@Directive({
  selector: "[appFocusFirstEmptyInput]"
})
export class FocusFirstEmptyInputDirective implements AfterViewInit {
  constructor(private el: ElementRef) {}
  ngAfterViewInit(): void {
    const invalidControl = this.el.nativeElement.querySelector(".ng-untouched");
    if (invalidControl) {
      invalidControl.focus();
    }
  }
}

AkRoy
quelle
0

Ich hatte mit vielen dieser Lösungen in allen Browsern nicht viel Glück. Dies ist die Lösung, die für mich funktioniert hat.

Für Routerwechsel:

router.events.subscribe((val) => {
    setTimeout(() => {
        if (this.searchElement) {
            this.searchElement.nativeElement.focus();
        }
    }, 1);
})

Dann ngAfterViewInit()für das Onload-Szenario.

rtaft
quelle
0

Sie können $ (jquery) verwenden:

<div>
    <form role="form" class="form-horizontal ">        
        <div [ngClass]="{showElement:IsEditMode, hidden:!IsEditMode}">
            <div class="form-group">
                <label class="control-label col-md-1 col-sm-1" for="name">Name</label>
                <div class="col-md-7 col-sm-7">
                    <input id="txtname`enter code here`" type="text" [(ngModel)]="person.Name" class="form-control" />

                </div>
                <div class="col-md-2 col-sm-2">
                    <input type="button" value="Add" (click)="AddPerson()" class="btn btn-primary" />
                </div>
            </div>
        </div>
        <div [ngClass]="{showElement:!IsEditMode, hidden:IsEditMode}">
            <div class="form-group">
                <label class="control-label col-md-1 col-sm-1" for="name">Person</label>
                <div class="col-md-7 col-sm-7">
                    <select [(ngModel)]="SelectedPerson.Id"  (change)="PersonSelected($event.target.value)" class="form-control">
                        <option *ngFor="#item of PeopleList" value="{{item.Id}}">{{item.Name}}</option>
                    </select>
                </div>
            </div>
        </div>        
    </form>
</div>

dann in ts:

    declare var $: any;

    @Component({
      selector: 'app-my-comp',
      templateUrl: './my-comp.component.html',
      styleUrls: ['./my-comp.component.css']
    })
    export class MyComponent  {

    @ViewChild('loadedComponent', { read: ElementRef, static: true }) loadedComponent: ElementRef<HTMLElement>;

    setFocus() {
    const elem = this.loadedComponent.nativeElement.querySelector('#txtname');
          $(elem).focus();
    }
    }
HamidReza
quelle
+1 für den einzigen, der @ViewChild verwendet, aber Sie benötigen keine Abfrage, wenn Sie bereits einen Verweis auf das Element (oder dessen Container) haben.
Nieminen