为什么使用* ngIf时模板局部变量在模板中不可用? [英] Why template local variables are not usable in templates when using *ngIf?

查看:74
本文介绍了为什么使用* ngIf时模板局部变量在模板中不可用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在引用可以隐藏/销毁"的输入时(因为使用了*ngIf并且某些元素被破坏了),使用井号标签语法#(在示例中为#test)创建的局部变量下面的内容)即使页面中存在该元素也无法正常工作.

When referencing an input that can be hidden/"destroyed" (because the *ngIf is used and some of the elements are destroyed), the local variable created by the hashtag syntax # (#test in the example below) does not work, even when the element exists in the page.

代码是:

@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,因为没有任何内容传递给该函数.

The alert displays undefined, because nothing is passed to that function.

是否有解决方案才能使其正常工作? 我的目标是关注将要创建的元素.

Is there a solution to make it work? My goal is to focus an element that will be created.

马克·拉杰科克(Mark Rajcok)给出的解决方案: 使用afterViewInit编写一条指令,该指令使用elementRef并在该元素上调用.focus().

Solution given by Mark Rajcok: make a directive with an afterViewInit that uses elementRef and calls .focus() on the element.

有关第1部分的工作版本,请参见此插件: http://plnkr.co/edit/JmBBHMo1hXLe4jrbpVdv?p=preview

See this plunker for a working version of part 1: http://plnkr.co/edit/JmBBHMo1hXLe4jrbpVdv?p=preview

一旦解决了创建后聚焦"的问题,我需要一种重新聚焦组件的方法,例如在"test.focus()"中(其中#test是输入的局部变量名称,但不能像我之前演示的那样使用).

Once this problem of "focus after creation" is fixed, I need a way to re-focus() a component, like in "test.focus()" (where #test is the local variable name for the input, but cannot be used like that as I demonstrated before).

马克·拉杰科克(Mark Rajcok)给出的多个解决方案

Multiple solutions given by Mark Rajcok

推荐答案

对于焦点问题的解决方案,您可以创建属性指令focusMe:

As for a solution to the focus problem, you could create an attribute directive, 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;
  }
}

柱塞

更新1 :为重新对焦功能添加解决方案:

Update 1: Adding the solution for the re-focus feature:

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

柱塞

使用setTimeout()将focus属性重置为false的替代方法是在FocusDirective上创建事件/输出属性,并在调用focus()时创建emit()事件.然后,AppComponent将侦听该事件并重置focus属性.

An alternative to using setTimeout() to reset the focus property to false would be to create an event/output property on the FocusDirective, and emit() an event when focus() is called. The AppComponent would then listen for that event and reset the focus property.

更新2 :这是使用ViewChild添加重新聚焦功能的另一种/更好的方法.我们不需要以这种方式跟踪焦点状态,也不需要在FocusMe指令上使用input属性.

Update 2: Here's an alternative/better way to add the re-focus feature, using ViewChild. We don't need to track the focus state this way, nor do we need an input property on the FocusMe directive.

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

柱塞

更新3 :这是另一种不需要指令的替代方法,该指令仍使用ViewChild,但我们通过本地模板变量而不是属性指令访问子项(感谢@alexpods用于提示):

Update 3: Here's yet another alternative that does not require a directive, which still uses ViewChild, but we access the child via a local template variable rather than an attribute directive (thanks @alexpods for 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);
  }
}

柱塞

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

Update 4: I updated the code in Update 3 to use NgZone so that we don't cause Angular's change detection algorithm to run after the setTimeout() finishes. (For more on change detection, see this answer).

更新5 :我更新了上述插件中的代码,以使用Renderer使其成为网络工作者安全.不建议直接在nativeElement上访问focus().

Update 5: I updated the code in the above plunker to use Renderer to make it web worker safe. Accessing focus() directly on nativeElement is discouraged.

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

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

I learned a lot from this question.

这篇关于为什么使用* ngIf时模板局部变量在模板中不可用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆