¿Por qué las variables locales de la plantilla angular2 no se pueden utilizar en las plantillas cuando se utiliza *ngIf?
La parte 1 "# test " no está definida cuando se usa * ngIf
Cuando se hace referencia a una entrada que se puede ocultar/"destruir" (porque se usa el *ngIf y se destruyen algunos de los elementos), la variable local creada por la sintaxis del hashtag # (#test en el ejemplo a continuación) de angular2 no funciona, incluso cuando el elemento existe en la página.
El código era:
@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();
}
}
La alerta muestra "indefinido", porque no se pasa nada a esa función.
Hay una solución para hacer ¿funciona? Mi objetivo es enfocar un elemento que se va a crear.
Solución dada por Mark Rajcok: cree una directiva con un AfterViewInit que use ElementRef y calls .focus() en el elemento.
Vea este émbolo para una versión de trabajo de la parte 1: http://plnkr.co/edit/JmBBHMo1hXLe4jrbpVdv?p=preview
Parte 2 cómo volver a enfocar ese elemento después de la creación inicial
Una vez solucionado este problema de "enfoque después de la creación", necesito una forma de re-focus () un componente, como en "test.focus () " (donde #test es el nombre de la variable local para la entrada, pero no se puede usar así como demostré antes).
Múltiples soluciones dadas por Mark Rajcok
1 answers
En cuanto a una solución al problema de focus, puede crear una directiva de atributos, 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;
}
}
Actualización 1 : Adición de la solución para la función de reenfoque:
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);
}
}
Una alternativa a usar setTimeout()
para restablecer la propiedad focus a false
sería crear una propiedad event/output en la FocusDirective, y emit()
un evento cuando se llame a focus()
. El AppComponent lo haría a continuación, escuche ese evento y restablezca la propiedad focus.
Actualización 2: Aquí hay una forma alternativa/mejor de agregar la función de reenfoque, usando ViewChild. No necesitamos rastrear el estado de enfoque de esta manera, ni necesitamos una propiedad de entrada en la directiva 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();
}
}
Actualización 3: Aquí hay otra alternativa que no requiere una directiva, que todavía usa ViewChild, pero accedemos al hijo a través de un local variable de plantilla en lugar de una directiva de atributos (gracias @ alexpods por la sugerencia):
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);
}
}
Actualización 4 : Actualizé el código en la actualización 3 para usar NgZone para que no causemos que el algoritmo de detección de cambios de Angular se ejecute después de que finalice setTimeout()
. (Para más información sobre la detección de cambios, vea esta respuesta ).
Actualización 5: Actualizé el código en el plunker anterior para usar Renderer para convertirlo en web worker seguro. Se desaconseja acceder a focus()
directamente en nativeElement
.
focusInput1() {
this._renderer.invokeElementMethod(
this.input1ElementRef.nativeElement, 'focus', []);
}
Aprendí mucho de esta pregunta.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:55:07