如何将焦点集中到另一个输入框?

33

当某些事件发生时,我需要能够将焦点切换到一个输入元素。在Angular 2中如何实现?

例如:

<input (keyUp)="processKeyUp($event)"/>
<input (focusme)="alert('i am focused')"/>

当第一个输入框按下某个键时,我希望能够将焦点集中到第二个输入框。我认为我需要使用自定义事件(代码段中的focusme),但我不知道在哪里或如何声明它,以及是否需要使用@Directive注释来声明它,或者以某种方式在组件中包含其定义。简而言之,我被难住了。

更新

忘了提一下,我知道可以在html中使用局部变量来实现这一点,但是我想要能够从组件中实现它,并且我想要在触发focusme事件时执行复杂的逻辑,以便监听该事件的控件可以确定它是否适用于它们。谢谢!


你不能仅仅通过扩展输入元素来创建自定义事件,你需要创建一个包装输入控件的自定义元素,该元素可以触发 focusme 事件,例如:<my-custom-element (focusme)="..."/>。 - Ajden Towfeek
好的,当我获得事件后,如何将焦点设置到我的自定义组件中的输入元素?您能在回答中提供一个示例吗? - Aviad P.
3
查看https://dev59.com/tFsW5IYBdhLWcg3w4aX7#34503163,有几种不同的方法/实现可以将焦点设置在另一个元素上。 - Mark Rajcok
4个回答

30
你可以将第二个输入作为变量传递给第一个输入并完成此操作。
例如: HTML
<!-- We pass focusable as a parameter to the processKeyUp function -->
<input (keyup)="processKeyUp($event, focusable)"/>

<!-- With #focusable we are creating a variable which references to the input -->
<input #focusable /> 

在你的 js/ts 代码稍后

@Component({
  selector: 'plunker-app'
})
@View({
  templateUrl: 'main.html'
})
class PlunkerApp {

  constructor() {
  }

  processKeyUp(e, el) {
    if(e.keyCode == 65) { // press A
      el.focus();
    }
  }
}

el是原始元素,因此您可以在其上使用纯JavaScript。

这里有一个plnkr,这样您就可以看到它的工作原理。

希望能对您有所帮助。


谢谢你的回答,但我需要焦点传递到另一个组件中的输入元素,因此一个组件触发事件,另一个组件对事件进行操作并将焦点设置在其中的输入元素上。 - Aviad P.
您可以同时阅读此线程 https://github.com/angular/angular/issues/3186 - Eric Martinez
1
@AviadP.,如果您想要进行组件间通信,并且您的组件之间没有“关联”(即没有祖先或后代模式),您可以在服务中放置一个EventEmitter,将该服务注入到两个组件中,让一个组件触发事件,另一个组件(您想要关注的那个)订阅它。这里是一个示例:https://dev59.com/J1sW5IYBdhLWcg3w-rTB#34402436。 - Mark Rajcok
由于某些原因,这个答案在我的代码中没有起作用(使用rc.3)。我还需要从组件的代码中聚焦元素,而不仅仅是从DOM触发的事件,所以这里接受的答案正是我所需要的。请注意更新内容,它们有很有用的提示。 - Lucio Mollinedo
在输入验证(响应式表单)和 cleaveJS 中非常有效。 - Tabares

19

为了操纵DOM元素,请始终尝试使用指令。在这种情况下,您可以编写简单的指令。

要从指令访问DOM,我们可以通过指令构造函数中的 ElementRef 注入主机DOM元素的引用。

constructor(@Inject(ElementRef) private element: ElementRef) {}

为了检测所绑定的值的变化,我们可以使用 ngOnChanges 生命周期方法。

protected ngOnChanges() {}

其它的东西都很简单。

简单的解决方案

// Simple 'focus' Directive
import {Directive, Input, ElementRef} from 'angular2/core';
@Directive({
    selector: '[focus]'
})
class FocusDirective {
    @Input()
    focus:boolean;
    constructor(@Inject(ElementRef) private element: ElementRef) {}
    protected ngOnChanges() {
        this.element.nativeElement.focus();
    }
}

// Usage
@Component({
    selector : 'app',
    template : `
        <input [focus]="inputFocused" type="text">
        <button (click)="moveFocus()">Move Focus</button>
    `,
    directives: [FocusDirective]
})
export class App {
    private inputFocused = false;
    moveFocus() {
        this.inputFocused = true;
        // we need this because nothing will 
        // happens on next method call, 
        // ngOnChanges in directive is only called if value is changed,
        // so we have to reset this value in async way,
        // this is ugly but works
        setTimeout(() => {this.inputFocused = false});
    }
}

使用EventEmitter解决方案

为了解决 setTimeout(() => {this.inputFocused = false}); 的问题,我们可以将指令绑定到事件源 - EventEmitter 或 Observable。以下是EventEmitter的使用示例。

// Directive
import {Directive, EventEmitter, Input, ElementRef} from 'angular2/core';

@Directive({
    selector: '[focus]'
})
class FocusDirective {
    private focusEmitterSubscription;   
    // Now we expect EventEmitter as binded value
    @Input('focus')
    set focus(focusEmitter: EventEmitter) {
        if(this.focusEmitterSubscription) {
            this.focusEmitterSubscription.unsubscribe();
        }
        this.focusEmitterSubscription = focusEmitter.subscribe(
            (()=> this.element.nativeElement.focus()).bind(this))
    }    
    constructor(@Inject(ElementRef) private element: ElementRef) {}
}

// Usage
@Component({
    selector : 'app',
    template : `
        <input [focus]="inputFocused" type="text">
        <button (click)="moveFocus()">Move Focus</button>
    `,
    directives: [FocusDirective]
})
class App {
    private inputFocused = new EventEmitter();
    moveFocus() {
        this.inputFocused.emit(null);    
    }
}

这两个解决方案都可以解决您的问题,但第二个性能略好,并且看起来更漂亮。


4
这个答案看起来非常类似于我的最近一篇答案,我想说有点太相似了。建议您再次查看我的答案,更新2和3提供了更好的实现方法。 - Mark Rajcok
2
抱歉,马克,我没有注意到你在评论中的回答。第一个解决方案与您的条目完全相同。我也喜欢您使用@ViewChild的解决方案,但在某些情况下(例如嵌套未知组件),指令/事件发射器解决方案更加灵活。根据用例,我看到每个解决方案的空间。 - majo
1
值得注意的是,任何订阅可观察对象的指令都应该是一个良好的组件,需要在 ngOnDestroy 生命周期钩子中取消订阅。 - David M.

10

实际上,你不需要编写任何TS代码来实现这个功能(我正在使用另一个回答的示例):

<input (keyup.enter)="focusable.focus()"/>
<input #focusable /> 

5

设置焦点 - Angular 2/5

<input type="text" [(ngModel)]="term2" #inputBox>

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

@Component({
  selector: 'app-general',
  templateUrl: './general.component.html',
  styleUrls: ['./general.component.scss']
})
export class GeneralComponent {

  @ViewChild("inputBox") _el: ElementRef;

  setFocus() {
    this._el.nativeElement.focus();
  }
}

在页面加载时设置焦点
ngAfterViewInit() {
    this._el.nativeElement.focus();
  }

您可以相应地更新此逻辑以响应按键。

更多信息


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接