当引用可以隐藏/“销毁”的输入(因为使用了* ngIf并且某些元素被销毁),由angular2的主题标签语法#(#test在下面的示例中)创建的局部变量不起作用,即使元素存在于页面中。
代码是:
@Component({
selector: 'my-app',template: `<h1>My First Angular 2 App</h1>
<button (click)="focusOther(test)">test</button>
<input #test *ngIf="boolValue" >
`
})
export class AppComponent {
private isVisible = false;
focusOther(testElement){
this.isVisible = true;
alert(testElement);
testElement.focus();
}
}
警报显示“undefined”,因为没有任何内容被传递给该功能。
是否有解决方案使其工作?
我的目标是集中一个将被创建的元素。
Mark Rajcok提供的解决方案:使用一个使用elementRef并在元素上调用.focus()的afterViewInit指令。
看到这个plunker的第1部分的工作版本:
http://plnkr.co/edit/JmBBHMo1hXLe4jrbpVdv?p=preview
第2部分如何在初始创建后重新聚焦该元素
一旦这个“创建后聚焦”的问题被修复,我需要一种方法来重新聚焦()一个组件,如“test.focus()”(其中#test是输入的局部变量名,但不能像以前演示过的那样使用)。
Mark Rajcok提供了多种解决方案
import {Component,Directive,ElementRef} from 'angular2/core';
@Directive({
selector: '[focusMe]'
})
export class FocusDirective {
constructor(private el: ElementRef) {}
ngAfterViewInit() {
this.el.nativeElement.focus();
}
}
@Component({
selector: 'my-app',directives: [FocusDirective],template: `<h1>My First Angular 2 App</h1>
<button (click)="toggle()">toggle</button>
<input focusMe *ngIf="isVisible">
`
})
export class AppComponent {
constructor() { console.clear(); }
private isVisible = false;
toggle() {
this.isVisible = !this.isVisible;
}
}
Plunker
更新1:添加重新聚焦功能的解决方案:
import {Component,ElementRef,Input} from 'angular2/core';
@Directive({
selector: '[focusMe]'
})
export class FocusMe {
@Input('focusMe') hasFocus: boolean;
constructor(private elementRef: ElementRef) {}
ngAfterViewInit() {
this.elementRef.nativeElement.focus();
}
ngOnChanges(changes) {
//console.log(changes);
if(changes.hasFocus && changes.hasFocus.currentValue === true) {
this.elementRef.nativeElement.focus();
}
}
}
@Component({
selector: 'my-app',template: `<h1>My First Angular 2 App</h1>
<button (click)="showinput()">Make it visible</button>
<input *ngIf="inputIsVisible" [focusMe]="inputHasFocus">
<button (click)="focusinput()" *ngIf="inputIsVisible">Focus it</button>
`,directives:[FocusMe]
})
export class AppComponent {
private inputIsVisible = false;
private inputHasFocus = false;
constructor() { console.clear(); }
showinput() {
this.inputIsVisible = true;
}
focusinput() {
this.inputHasFocus = true;
setTimeout(() => this.inputHasFocus = false,50);
}
}
Plunker
使用setTimeout()将focus属性重置为false的替代方法是在FocusDirective上创建一个事件/输出属性,并在调用focus()时发出一个事件。然后AppComponent将监听该事件并重置focus属性。
更新2:这是使用ViewChild添加重点焦点功能的替代/更好的方式。我们不需要以这种方式跟踪焦点状态,也不需要在FocusMe指令上输入属性。
import {Component,Input,ViewChild} from 'angular2/core';
@Directive({
selector: '[focusMe]'
})
export class FocusMe {
constructor(private elementRef: ElementRef) {}
ngAfterViewInit() {
// set focus when element first appears
this.setFocus();
}
setFocus() {
this.elementRef.nativeElement.focus();
}
}
@Component({
selector: 'my-app',template: `<h1>My First Angular 2 App</h1>
<button (click)="showinput()">Make it visible</button>
<input *ngIf="inputIsVisible" focusMe>
<button (click)="focusinput()" *ngIf="inputIsVisible">Focus it</button>
`,directives:[FocusMe]
})
export class AppComponent {
@ViewChild(FocusMe) child;
private inputIsVisible = false;
constructor() { console.clear(); }
showinput() {
this.inputIsVisible = true;
}
focusinput() {
this.child.setFocus();
}
}
Plunker
更新3:这是另一个不需要使用ViewChild的指令的替代方法,但是我们通过一个本地模板变量而不是一个属性指令来访问该子进程(感谢@alexpods为the tip):
import {Component,ViewChild,ngzone} from 'angular2/core';
@Component({
selector: 'my-app',template: `<h1>Focus test</h1>
<button (click)="showinput()">Make it visible</button>
<input #input1 *ngIf="input1IsVisible">
<button (click)="focusInput1()" *ngIf="input1IsVisible">Focus it</button>
`,})
export class AppComponent {
@ViewChild('input1') input1ElementRef;
private input1IsVisible = false;
constructor(private _ngzone: ngzone) { console.clear(); }
showinput() {
this.input1IsVisible = true;
// Give ngIf a chance to render the <input>.
// Then set the focus,but do this outside the Angualar zone to be efficient.
// There is no need to run change detection after setTimeout() runs,// since we're only focusing an element.
this._ngzone.runOutsideAngular(() => {
setTimeout(() => this.focusInput1(),0);
});
}
setFocus(elementRef) {
elementRef.nativeElement.focus();
}
ngDoCheck() {
// if you remove the ngzone stuff above,you'll see
// this log 3 times instead of 1 when you click the
// "Make it visible" button.
console.log('doCheck');
}
focusInput1() {
this.setFocus(this.input1ElementRef);
}
}
Plunker
更新4:我更新了更新3中的代码以使用ngzone,以便在setTimeout()完成后,我们不会导致Angular的更改检测算法运行。 (有关更改检测的更多信息,请参阅this answer)。
更新5:我更新了上面的代码,使用Renderer来使web worker安全。不要直接在nativeElement上访问focus()。
focusInput1() {
this._renderer.invokeElementMethod(
this.input1ElementRef.nativeElement,'focus',[]);
}
我从这个问题中学到很多东西。