如何在 Angular 2 中打开模态元素后立即将焦点设置在模态元素上?

How to set focus on the elements of the modal as soon as they are opened in Angular 2?

我是 Angular JS 的新手并且有了 Angular 2 的新版本,我在执行指令时遇到了麻烦,该指令可以处理打开的模态上的强制焦点单击按钮。

以前问过几个类似的问题,答案在Angular1中如下:

app.directive('focusMe',
    ['$timeout',
        function ($timeout) {
            return {
                link: {
                    pre: function preLink(scope, element, attr) {
                      // ...
                    },
                    post: function postLink(scope, element, attr) {
                        $timeout(function () {
                            element[0].focus();
                        }, 0);

                    }
                }
            }
        }]);
});

我正在尝试转换 Angular 2 中的同一段代码。但我不确定如何实现它。谁能给我更多关于如何实现这一目标的信息,为我指明正确的方向。

编辑:

我尝试按如下方式实现该指令,当我调试代码时,我什至看到正在调用该指令,但我仍然无法将焦点放在模式对话框的元素上:

import { Directive, ElementRef } from "@angular/core";

@Directive({
    selector: "[ModFocus]"
})

export class ModalFocus {

    constructor(private _el: ElementRef) {

        this._el.nativeElement.focus();
    }

}

我是不是做错了什么?或者除了在 nativeElement 上调用 focus() 之外,我还需要做其他事情吗?

HTML 模态:

<div class="modal-dialog modal-sm" tabindex="-1">
        <div class="modal-content">
            <div class="modal-header">
                <h4 class="modal-title">Are you sure?</h4>
            </div>
            <div class="modal-body">
                Warning
            </div>
        <div class="modal-footer ok-cancel" ModFocus>
                <button type="button" class="btn btn-default" (click)="cancel()">Cancel</button>
                <button type="button" class="btn btn-primary" (click)="delete()" data-dismiss="modal">Delete</button>
            </div>
        </div>
    </div>

谢谢。

当您使用 Angular 2 创建组件时,您使用的语法与 AngularJS 不同。您的示例代码可能如下所示:

import { Component, ElementRef, ViewChild } from "@angular/core";

@Component({
    selector: "whatever",
    template: `<input #myControl type='input'/>`
})
export class MyComponent {
    @ViewChild("myControl") myCtrl: ElementRef;

    constructor () {
        // This is wrong. It should be done in ngOnInit not in the
        // constructor as the element might not yet be available.
        //this.myCtrl.nativeElement.focus();   
    }
}

我是在没有检查的情况下从脑海中写下这篇文章的,但这应该让您对应该前进的方向有一个很好的了解。

更新:

在您更新问题时,我认为这里有一些错误。在您的新代码中,您将焦点设置在构造函数中。可能是您的视图仍未生成,因此您要设置焦点的元素仍然不可用(在我之前的示例中,我在构造函数中实例化它时误导了您,当我想要 OnInit。对此我深表歉意。)。我会做以下事情:

import { Directive, ElementRef, OnInit } from "@angular/core";

@Directive({
    selector: "[ModFocus]"
})

export class ModalFocus implements OnInit {

    constructor(private _el: ElementRef) {

    }

    ngOnInit(): any {
        // At this point, your element should be available.
        this._el.nativeElement.focus();
    }
}

OnInit 是 Angular 2 发出的 lifecycle hooks 之一。我建议您仔细阅读它们以便在调用它们时获得更好的理解。

更新 2:

问题是 Directives 没有模板。对添加它们的元素执行操作。创建指令时,其构造函数如下所示:

构造函数(私有 el:ElementRef,私有渲染器:Renderer){ }

el 应该可以访问 this.el.nativeElement.focus()

看看 angular.io 上关于 Attribute Directives

的这篇文章

我在问题中对指令的执行实际上是正确的。焦点没有进入模式的问题是因为 tabindex = -1 放置错误。

以下是我创建的指令。我没有使用 ElementRef anymore directly. Instead, I used the Renderer as Angular docs clearly mentioned to avoid the classes which are tagged with security risk

import { Directive, ElementRef, Renderer} from "@angular/core";
@Directive({
    selector: "[ModFocus]"
})

export class modalFocus {
    constructor(private _el: ElementRef, private renderer: Renderer) {
        this.renderer.invokeElementMethod(this._el.nativeElement, 'focus');
    }

}

HTML:

<div class="modal fade" tabindex="-1"> 
<div class="modal-dialog modal-sm">
        <div class="modal-content">
            <div class="modal-header">
                <h4 class="modal-title">Are you sure?</h4>
            </div>
            <div class="modal-body">
                Warning
            </div>
        <div class="modal-footer ok-cancel">
                <button type="button" class="btn btn-default" (click)="cancel()" ModFocus>Cancel</button>
                <button type="button" class="btn btn-primary" (click)="delete()" data-dismiss="modal">Delete</button>
            </div>
        </div>
    </div>
</div>

在上面的 html 中,我实际上缺少主标签上带有 class 模态淡入淡出的 tabindex。在此处添加 tabindex 时,焦点会转移到模态框上的按钮上。

Husein 提供了非常有用的宝贵意见。因此,我接受他的回答。再次感谢侯赛因。

trapFocus(){
    // add all the elements inside modal which you want to make focusable
const  focusableElements = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
const modal:any = document.querySelector('#deactivate-modal'); // select the modal by it's id

const firstFocusableElement = modal.querySelectorAll(focusableElements)[0]; // get first element to be focused inside modal
const focusableContent = modal.querySelectorAll(focusableElements);
const lastFocusableElement = focusableContent[focusableContent.length - 1]; // get last element to be focused inside modal


document.addEventListener('keydown', function(e) {
let isTabPressed = e.key === 'Tab' || e.keyCode === 9;

if (!isTabPressed) {
return;
}

if (e.shiftKey) { // if shift key pressed for shift + tab combination
if (document.activeElement === firstFocusableElement) {
  lastFocusableElement.focus(); // add focus for the last focusable element
  e.preventDefault();
}
} else { // if tab key is pressed
if (document.activeElement === lastFocusableElement) { // if focused has reached to last focusable element then focus first focusable element after pressing tab
  firstFocusableElement.focus(); // add focus for the first focusable element
  e.preventDefault();
}
}
});

firstFocusableElement.focus();

  }