2015-12-29 27 views
29

第1部分「#TEST」是不確定使用時* ngIf

當引用的可以隱藏/「破壞」(輸入,因爲* ngIf並且一些元素被銷燬),那麼即使元素存在於頁面中,angular2的hashtag語法#(在下面的示例中爲#test)創建的局部變量也不起作用。爲什麼angular2模板局部變量使用時,無法在模板中使用* ngIf

的代碼是:

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

} 

警報顯示「未定義」,因爲沒有被傳遞給函數。

有沒有解決方案,使其工作? 我的目標是聚焦一個將被創建的元素。馬克Rajcok給出

解決方案:作出指示與使用elementRef並調用.focus()元素的一個afterViewInit。

請參閱本plunker爲第1部分工作版本: http://plnkr.co/edit/JmBBHMo1hXLe4jrbpVdv?p=preview

第2部分如何重新聚焦該元素的初始創建

後一旦「創建後對焦」這個問題是固定的,我需要一種重新聚焦()組件的方法,就像在「test.focus()」(其中#test是輸入的局部變量名稱,但不能象我之前演示的那樣使用)。馬克Rajcok給出

多解

+0

另請參見https://github.com/angular/angular/issues/6179 –

+0

我已經填充了有關角度回購的問題,因爲如果我們考慮實際的文檔說明「我們可以引用本地模板變量相同的元素,同胞元素或其任何子元素。「。 –

回答

45

至於解決的重點問題,您可以創建一個屬性指令,focusMe

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, Directive, 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()到聚焦屬性重置爲false是創建一個事件/輸出屬性的替代在FocusDirective上,以及emit()focus()被調用的事件。然後,AppComponent將偵聽該事件並重置焦點屬性。

更新2:這是使用ViewChild添加重新聚焦功能的替代方法/更好的方法。我們不需要這樣跟蹤焦點狀態,也不需要FocusMe指令中的輸入屬性。

import {Component, Directive, ElementRef, 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()結束後運行。 (關於更改檢測的更多信息,請參閱this answer)。

更新5:我更新了上述plunker中的代碼,以使用Renderer使其成爲web worker安全。不鼓勵在nativeElement上直接訪問focus()

focusInput1() { 
    this._renderer.invokeElementMethod(
    this.input1ElementRef.nativeElement, 'focus', []); 
} 

我從這個問題中學到了很多東西。

+0

我在那之後出現了另一個問題,我怎麼能在初始化之後,將AppComponent的代碼的函數中的那個輸入回送給那個輸入?一旦可用,我就會更新重擊器。除了在指令中創建自定義事件,沒有其他解決方案,然後將其與組件綁定,因此當該事件觸發時,焦點返回到輸入? –

+1

@MykaEyl,看我更新的答案。 –

+1

太棒了!我喜歡@ViewChild('input1')的用法,因爲我覺得如果我不得不使用自定義指令來進行選擇,那真的很糟糕。謝謝! –

相關問題