使用* ngIf时,为什么模板局部变量在模板中不可用?

时间:2015-12-29 00:27:44

标签: angular

第1部分:使用* ngIf

时,“#test”未定义

当引用可以隐藏/“销毁”的输入时(因为使用了*ngIf并且某些元素被销毁),由hashtag语法#创建的局部变量({{即使元素存在于页面中,下面示例中的1}}也不起作用。

代码是:

#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(); } } ,因为没有任何内容传递给该功能。

是否有解决方案使其有效? 我的目标是集中一个将要创建的元素。

Mark Rajcok提供的解决方案: 使用postReInit制作一个指令,该指令使用elementRef并在元素上调用.focus()。

请参阅此plunker以获取第1部分的工作版本: http://plnkr.co/edit/JmBBHMo1hXLe4jrbpVdv?p=preview

第2部分如何在初始创建后重新聚焦该元素

一旦修复了“创建后焦点”这个问题,我需要一种重新聚焦()组件的方法,比如“test.focus()”(其中#test是输入的局部变量名,但不能像我之前所说的那样使用。)

Mark Rajcok提供的多种解决方案

1 个答案:

答案 0 :(得分:46)

至于焦点问题的解决方案,您可以创建属性指令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 for { {3}}):

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

the tip

更新4 :我更新了Update 3中的代码以使用NgZone,这样我们就不会在setTimeout()完成后运行Angular的更改检测算法。 (有关更改检测的更多信息,请参阅Plunker)。

更新5 :我更新了上面的plunker中的代码,以使用Renderer使其更安全。 focus()。

nativeElement

我从这个问题中学到了很多东西。

相关问题