【问题标题】:How to set focus on element with binding?如何通过绑定将焦点设置在元素上?
【发布时间】:2019-10-21 22:53:25
【问题描述】:

在 Angular2 中,我如何设置元素焦点的绑定。 我不想用 elementRef 设置它。 我认为在 AngularJS 中有 ngFocus 指令 在 Angular2 中没有这样的指令

【问题讨论】:

    标签: angular


    【解决方案1】:

    渲染器服务现在是 deprecated(从 Angular 4.x 开始) 新的 Renderer2 服务没有 invokeElementMethod。 您可以做的是获取对元素的引用,如下所示:

    const element = this.renderer.selectRootElement('#elementId');
    

    然后你可以像这样使用它来关注那个元素:

    element.focus();
    

    更多关于selectRootElement如何工作here

    编辑:

    如果元素没有获得焦点,常见的问题是元素还没有准备好。 (例如:禁用、隐藏等)。你可以这样做:

    setTimeout(() => element.focus(), 0);
    

    这将创建一个将在下一个 VM 轮次中运行的宏任务,因此如果您启用该元素,焦点将正常运行。

    【讨论】:

    • 我们使用renderer2.selectRootElement得到的元素引用与使用ViewChild得到的元素引用有何不同?我试图理解为什么使用 ViewChild 获取元素引用(假设我们将其分配给'abc')然后使用 abc.nativeElement.focus() 将焦点设置在元素上会带来 XSS 安全风险,但使用 renderer2 不会't。
    • 此外,使用 nativeElement 是否仅在使用组件访问其模板中的元素时会带来安全风险,或者在自定义指令中使用它来执行相同操作时也会带来安全风险任何其他组件模板中的元素?
    • @user6860460 使用 renderer2 或通过 Angular 提供给您的任何其他抽象可能会更安全,因为您不直接使用 DOM。但是,当您使用 nativeElement 时,您再次打开了风险。这个例子不适合这种情况。如果您要使用 renderer2 的 addClass 或 createElement 等方法,而不是对原始 DOM 元素执行此操作,则认为它更安全。 angular.io/api/core/ElementRef 阅读底部的警告。这意味着您应该尽可能使用 renderer2 提供的 API,但是一旦访问 nativeElement 就会有风险。
    • 但是,如果您在 Renderer2 方法中访问 nativeElement,您会面临同样的风险吗?谢谢。
    • @user6860460 技术上是的。一旦你有了 nativeElement,你就可以用它做任何你想做的事情。就像直接设置innerHTML参数并注入一些代码一样。
    【解决方案2】:

    一个简单的“焦点”指令

    import {Directive, Input, ElementRef} from 'angular2/core';
    @Directive({
        selector: '[focus]'
    })
    class FocusDirective {
        @Input()
        focus:boolean;
        constructor(@Inject(ElementRef) private element: ElementRef) {}
        protected ngOnChanges() {
            this.element.nativeElement.focus();
        }
    }
    
    // Usage
    @Component({
        selector : 'app',
        template : `
            <input [focus]="inputFocused" type="text">
            <button (click)="moveFocus()">Move Focus</button>
        `,
        directives: [FocusDirective]
    })
    export class App {
        private inputFocused = false;
        moveFocus() {
            this.inputFocused = true;
            // we need this because nothing will 
            // happens on next method call, 
            // ngOnChanges in directive is only called if value is changed,
            // so we have to reset this value in async way,
            // this is ugly but works
            setTimeout(() => {this.inputFocused = false});
        }
    }
    

    【讨论】:

    • 一点提示。不要直接使用nativeElement。这不与网络工作者一起保存。
    • directives 属性已在 RC 6 中删除。您应该将 FocusDirective 移动到 NgModule 装饰器的声明属性中。
    • 我也有同样的要求,但是我想在指令中移动 setTimeout,是否可以通过在指令中使用 setTimeout 来实现相同的功能?
    • 这适用于基于桌面的浏览器,但由于某种原因不适用于基于移动的浏览器。你知道为什么吗?
    【解决方案3】:

    我尝试了两种变体,但没有一个适合简单使用。第一个(@AngJobs)需要在您使用指令(设置焦点 = true)的组件中进行额外的工作,第二个(@ShellZero)根本不工作,因为在视图实际准备好之前调用焦点。所以我将焦点呼叫转移到ngAfterViewInit。现在您可以添加&lt;input focus... 并忘记它。元素会在视图初始化后自动获得焦点。

    import { Directive, ElementRef, Renderer, AfterViewInit } from '@angular/core';
    @Directive({
        selector: '[focus]'
    })
    
    export class DmFocusDirective implements AfterViewInit {
        constructor(private _el: ElementRef, private renderer: Renderer) {
        }
    
        ngAfterViewInit() {
            this.renderer.invokeElementMethod(this._el.nativeElement, 'focus');
        }
    }
    

    【讨论】:

    • 谢谢 - 这太棒了。尽管您的类名不一定需要前缀(就像您使用 Dm 所做的那样)。应用前缀的关键位是选择器名称值。即selector: '[appFocus]'。我想这会减少后续角度版本等中发生冲突的可能性。
    • 注意:Renderer 已被弃用,Angular 团队不打算将 invokeElementMethod() 转发到 Renderer2github.com/angular/angular/issues/13818
    【解决方案4】:

    来自@MrBlaise,我使用了 setTimeout sn-p,它为我完成了以下工作。

    <input type="text" #searchInput />
    
    
    
    import { ElementRef, ViewChild } from '@angular/core';
    
    ...
    
    @ViewChild('searchInput') private searchInput: ElementRef;
    
    ...
    
    setTimeout(() => this.searchInput.nativeElement.focus(), 0);
    

    【讨论】:

      【解决方案5】:

      使用 angular2 将焦点设置在元素上的最佳方法是使用 Renderer 并调用元素上的方法。没有elementRef,就无法做到这一点。

      结果如下:

      this.renderer.invokeElementMethod(this.input.nativeElement, 'focus', []);
      

      renderer 使用 protected renderer : Renderer 注入到构造函数中

      【讨论】:

      • 我认为更好的方法是编写我自己的 ngFocus 指令来操作元素而不是在组件类中进行。
      • 这是一种暗示......并且代码保持不变
      • 但为什么 Angular2 团队没有创建它?
      • 因为每个人都想要一个稍微不同的实现。这样您就可以自己实现它,并了解有关 angular2 的更多信息
      【解决方案6】:

      更简单的方法:

      import { Directive, ElementRef, Renderer} from "@angular/core";
      @Directive({
          selector: "[Focus]"
      })
      
      export class myFocus {
          constructor(private _el: ElementRef, private renderer: Renderer) {
              this.renderer.invokeElementMethod(this._el.nativeElement, 'focus');
          }
      
      }
      

      【讨论】:

      • 不适合我,因为在视图实际准备好之前调用了焦点
      • 然后你必须将它添加到 ngOnInit() [The Angular LifeCycle Hooks] 中,或者你必须订阅你拥有的任何事件,以便“你在事件发生时集中注意力”。
      【解决方案7】:

      它对我有用,但在控制台中有错误。 经过调试和搜索发现这篇文章:https://www.picnet.com.au/blogs/guido/post/2016/09/20/angular2-ng2-focus-directive/

      只需复制粘贴即可。它对我来说非常有效。

      import {Directive, AfterViewInit, ElementRef, DoCheck} from '@angular/core';
      
      @Directive({selector: '[focus]'})
      export class FocusDirective implements AfterViewInit, DoCheck {
          private lastVisible: boolean = false;
          private initialised: boolean = false;
      
          constructor(private el: ElementRef) {
          }
      
          ngAfterViewInit() {
              console.log('inside FocusDirective.ngAfterViewInit()');
              this.initialised = true;
              this.ngDoCheck();
          }
      
          ngDoCheck() {
      
              console.log('inside FocusDirective.ngDoCheck()');
      
              if (!this.initialised) {
                  return;
              }
              const visible = !!this.el.nativeElement.offsetParent;
              if (visible && !this.lastVisible) {
                  setTimeout(() => {
                      this.el.nativeElement.focus();
                  }, 1);
              }
              this.lastVisible = visible;
          }
      }
      

      【讨论】:

        【解决方案8】:
        import { Directive, ElementRef, AfterViewChecked } from '@angular/core';
        
        @Directive({
         selector: '[autoFocus]',
        })
        export class FocusDirective implements AfterViewChecked {
        
          constructor(private _elementRef: ElementRef) { }
        
          ngAfterViewChecked() {
            this._elementRef.nativeElement.focus()
          }
        
        }
        

        【讨论】:

          【解决方案9】:

          诀窍是同时使用焦点和选择:

          this.(element).nativeElement.focus();
          this.(element).nativeElement.select();
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2018-10-05
            • 2018-03-25
            • 2011-01-24
            • 1970-01-01
            • 2021-02-17
            • 1970-01-01
            • 2020-03-27
            • 1970-01-01
            相关资源
            最近更新 更多