Angular2 - фокусування текстового поля на завантаженні компонентів


79

Я розробляю компонент у Angular2 (Бета 8). Компонент має текстове поле та випадаюче меню. Я хотів би встановити фокус у текстовому полі, як тільки компонент завантажується або в разі зміни спадного меню. Як би я досяг цього в angular2. Далі наводиться HTML для компонента.

<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>

Відповіді:


78

Використання простого autofocusатрибута HTML5 працює для сценарію "при завантаженні"

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

або

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

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


45
Здається, це працює, лише якщо ви оновите сторінку або завантажите сторінку з нуля. Якщо ви переходите до компонента форми через маршрутизатор, поле не вибирається.
Dennis W

3
це не є гарним рішенням, коли ми маємо кілька компонентів, це буде працювати лише для першого компонента за замовчуванням
Paresh Varde

+1 - Дякую Нікхіле, це ідеально; Я використовую це так: <div class = "or-name uk-tile-muted uk-text-left uk-display-block" * ngIf = "value! == cHttpUser"> {{value.name}} </ div> <input class = "uk-input uk-form-controls-text or-input" * ngIf = "value === cHttpUser" [(ngModel)] = "value.name" автофокус (клацання) = "оновити ( ) "(keyup.enter) =" update () "(keyup.tab) =" update () "(blur) =" update () ">: клацніть на div, введення внизу, щоб відразу сфокусуватись, перш ніж ні. Потрібно було натиснути ще раз, щоб увійти та набрати текст, це був "півдороги". Залишився один клік!
Олів'є

78

Ця відповідь натхнена post post Angular 2: Зосередьтеся на нещодавно доданому елементі введення

Кроки для встановлення фокусу на елементі Html у Angular2

  1. Імпортуйте ViewChildren у свій компонент

    import { Input, Output, AfterContentInit, ContentChild,AfterViewInit, ViewChild, ViewChildren } from 'angular2/core';
    
  2. Оголосіть ім'я змінної локального шаблону для html, для якого потрібно встановити фокус

  3. Реалізуйте функцію ngAfterViewInit () або інші відповідні хуки життєвого циклу
  4. Далі йде фрагмент коду, який я використав для встановлення фокусу

    ngAfterViewInit() {vc.first.nativeElement.focus()}
    
  5. Додайте #inputатрибут до елемента DOM, до якого ви хочете отримати доступ.

///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>


2
Досить просто, але що станеться, якщо вам потрібно встановити фокус на інший компонент? Наприклад, поле пошуку за замовчуванням (у заголовку) з дочірнього компонента.
Rick Strahl

2
Як щодо динамічно генерованого елемента?
Kaushik Thanki

Використовуючи Angular Material, мені потрібно було включити ChangeDetectorRefв конструктор, а потім зателефонувати this.cd.detectChanges();після виклику focus, згідно з цією відповіддю @jspassov .
Дерек Хілл

32
<input id="name" type="text" #myInput />
{{ myInput.focus() }}

це найкращий і найпростіший спосіб, оскільки код "myInput.focus ()" запускається після створення вводу

ПОПЕРЕДЖЕННЯ: це рішення є прийнятним, лише якщо у вас є один елемент у формі (користувач не зможе вибрати інші елементи)


3
Лол, що насправді працює! Трохи хакерський, але дуже простий і функціональний, дякую! :)
Вагнер Данда да Сілва Філхо

5
Спочатку мені це дуже сподобалось, але, здається, це завжди заважає мені відбирати інші елементи (або навіть виділяти текст для копіювання). Довелося перейти на прийняту відповідь тут з поглядом дітей.
Jason W

4
так, цей метод прийнятний, якщо на екрані є лише одне редагування. це перешкоджає фокусуванню на будь-яких інших входах!
Сергій Гурін

1
Зверніть увагу, що метод myInput.focus () буде спрацьовувати при кожному циклі змін. Це може спричинити небажану поведінку, як @JasonW.
Карстен

2
прихований дорогоцінний камінь !! :) Ви розгойдуєте мій друг!
Manish Jain

29

В оригінальному запитанні просився спосіб встановити фокус спочатку, АБО встановити фокус пізніше у відповідь на подію. Здається, правильний спосіб підійти до цього - зробити директиву атрибута, яку можна встановити для будь-якого елемента введення, а потім використовувати власну подію, щоб безпечно активувати метод фокусування на цьому елементі введення. Щоб це зробити, спочатку створіть директиву:

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', []);
        });
    }
}

Зверніть увагу, що він використовує метод renderer.invokeElementMethod на nativeElement, який є безпечним для веб-працівника. Також зверніть увагу, що focusEvent оголошено як вхідні дані.

Потім додайте наступні оголошення до компонента Angular 2, який має шаблон, де ви хочете використовувати нову директиву для встановлення фокусу на вхідний елемент:

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);
}

Не забудьте імпортувати EventEmitter над компонентом так:

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

і в шаблоні для цього компонента встановіть новий атрибут [focus] таким чином:

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

Нарешті, у своєму модулі імпортуйте та оголосіть нову директиву так:

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

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

Підсумок: функція ngAfterViewInit призведе до випуску нового EventEmitter, і оскільки ми призначили цей випромінювач атрибуту [focus] у вхідному елементі нашого шаблону, ми оголосили цей EventEmitter як вхід до нової директиви та викликали focus у функції стрілки, яку ми передали підписці на цю подію, елемент введення отримає фокус після ініціалізації компонента та кожного разу, коли буде викликаний setFocus

У мене була така сама потреба у власному додатку, і це спрацювало, як рекламується. Щиро дякую наступному: http://blog.thecodecampus.de/angular-2-set-focus-element/


Це чудово, і це працює, але мені цікаво, чи це просто маленька забагато роботи для незначного фокусування. Я збирався зробити це як приємну річ для користувача, оскільки це не є вимогою, але, схоже, надмірна кількість коду для збереження користувача одним клацанням миші.
Ка Мок,

3
@KentWeigel - Це справді стильне рішення і чудово працює. Це "правильна" відповідь imo. Дякую.
HockeyJ

це справді гарне рішення, але ви не використовували setFocusфункцію
Hakan Fıstık

6

Див. Angular 2: Зосередьтесь на нещодавно доданому елементі введення, щоб дізнатися, як встановити фокус.

Для завантаження використовуйте ngAfterViewInit()зворотний виклик життєвого циклу.


Дуже дякую. Посилання, яким ви поділилися, було дуже корисним. +1 за це. Поділюсь відповіддю на цей конкретний сценарій.
параг

2

У мене була трохи інша проблема. Я працював із входами в модальному режимі, і це зводило мене з розуму. Жодне із запропонованих рішень не спрацювало для мене.

Поки я не знайшов цю проблему: https://github.com/valor-software/ngx-bootstrap/issues/1597

Цей хороший хлопець підказав мені, що модальний ngx-bootstrap має конфігурацію фокусування. Якщо для цієї конфігурації не встановлено значення false, модаль буде сфокусований після анімації, і НЕМАЄ ШЛЯХА сфокусувати щось інше.

Оновлення:

Щоб встановити цю конфігурацію, додайте наступний атрибут до модального div:

[config]="{focus: false}"

Оновлення 2:

Щоб змусити зосередитись на полі введення, я написав директиву та встановив фокус у кожному циклі AfterViewChecked, доки поле введення має клас ng-недоторканий.

 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, ти міг би поділитися, де це налаштувати?
кіберабіс

як ви можете побачити тут: valor-software.com/ngx-bootstrap/#/modals#static є атрибут конфігурації [config] = "{backdrop: 'static'}" Ви можете встановити там властивість focus: false
FireGnome

Привіт, у мене така сама проблема, я додав модальний і конфігураційний ngx і спробував додати фокус за допомогою директиви (поки що не везе), що ви використовували для кожного фокусування на вході? Подяка
Саймон

2

Крім того, це можна робити динамічно так ...

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

Де вхід

const input = {
  id: "my-input",
  type: "text",
  autofocus: true
};

Навпаки у мене не вийшло[autofocus]="false"
teddybear

У вашому випадку це буде автофокус = “false”; [] призначені лише для оцінок типу "myObj.key". Ви використовуєте фактичний рядок.
SoEzPz

2

Директива про перше поле автофокусування

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();
    }
  }
}


0

Мені не пощастило з багатьма з цих рішень у всіх браузерах. Це рішення допомогло мені.

Для змін маршрутизатора:

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

Тоді ngAfterViewInit()для сценарію завантаження.


0

Ви можете використовувати $ (jquery):

<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>

то в 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();
    }
    }

+1 для єдиного, який використовує @ViewChild, але вам не потрібен jquery, якщо у вас вже є посилання на елемент (або його контейнер).
Nieminen
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.