chodzi o rozwiązanie problemu ostrości można utworzyć dyrektywę atrybutu 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
Update 1: Dodanie rozwiązanie dla funkcji re-focus:
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
Alternatywą korzystania setTimeout()
zresetować właściwość ostrości false
byłoby utworzyć właściwość zdarzenia/wyjścia na FocusDirective i emit()
zdarzenie, gdy jest wywoływana focus()
. AppComponent będzie nasłuchiwać tego zdarzenia i zresetować właściwość focus.
Aktualizacja 2: Oto alternatywny/lepszy sposób dodania funkcji ponownego ogniskowania za pomocą narzędzia ViewChild. Nie musimy śledzić stanu skupienia w ten sposób, ani nie potrzebujemy właściwości wejściowej w dyrektywie 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
Update 3: Tu jest jeszcze inna alternatywa, która nie wymaga dyrektywa, która nadal wykorzystuje ViewChild, ale mamy dostęp do dziecka poprzez lokalną zmienną szablonu, a nie dyrektywy atrybutu (dzięki @alexpods dla 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
Update 4: I uaktualniony kod Update 3 do korzystania NgZone tak, że nie powodują algorytm wykrywania zmian kątowych, aby uruchomić po setTimeout()
wykończeniach. (Aby uzyskać więcej informacji na temat wykrywania zmian, zobacz this answer).
Aktualizacja 5: Zaktualizowałem kod w powyższym plunkerze, aby użyć Renderera, aby uczynić go bezpiecznym w Internecie. Uzyskanie dostępu do focus()
bezpośrednio pod numerem nativeElement
jest odradzane.
focusInput1() {
this._renderer.invokeElementMethod(
this.input1ElementRef.nativeElement, 'focus', []);
}
Wiele się nauczyłem od tego pytania.
Zobacz także https://github.com/angular/angular/issues/6179 –
Wypełniłem ten problem na repozytorium kątowym, ponieważ jest to błąd, jeśli weźmiemy pod uwagę faktyczną dokumentację stwierdzającą: "Możemy odwoływać się do lokalnej zmiennej szablonu ten sam element, na elemencie rodzeństwa lub na jego elementach potomnych. ". –