3

I'm desperately trying to figure out the best way to add focus trap to my modals in an Angular 8 application. I've stumbled upon the Angular Material CDK and installed it for the A11y Accessibility tools only but I can't figure out how to import and use FocusTrap or FocusTrapFactory.

If I try to add either to my declarations or imports arrays in @NgModule I get errors. Installing them in the providers does nothing. There's nothing I see in the docs on how to specifically pull the tools in to use. I have cdkTrapFocus, cdkFocusRegionStart, cdkFocusInitial and cdkFocusRegionEnd all setup in a modal to test. I've tried just pulling in FocusTrap and FocusTrapFactory into my component only, but still nothing.

https://v8.material.angular.io/cdk/a11y/overview#focuskeymanager

Has anyone successfully gotten this to work? If I have to use the Material UI tools to get this to work I will need to find another solution for trapping focus in my modals.

My modal component is a simple wrapper I open and close by ID with a service. It's basically just like: https://jasonwatmore.com/post/2019/07/12/angular-8-custom-modal-window-dialog-box

qbert
  • 119
  • 2
  • 9

2 Answers2

11

All you need to do is import the a11y module from cdk in your module

import { A11yModule } from '@angular/cdk/a11y';

imports: [
   A11yModule  
]

Add the focus trap directives to the parent element of your dialog contents e.g. below is a snippet where the dialog contents are projected

  <div class="modal-content" cdkTrapFocus [cdkTrapFocusAutoCapture]="true">
    <ng-template *ngTemplateOutlet="contentsTmpl"></ng-template>
  </div>

Edit

so long as the angular injected dom elements are enclosing the modal contents it should work. https://jasonwatmore.com/post/2019/07/12/angular-8-custom-modal-window-dialog-box example has an issue.

The modal created in this example is always there in dom and cdk auto focus capture traps focus when element is created or destroyed!

I modified this example a bit and it works fine. Here is the modified example

https://stackblitz.com/edit/angular-8-custom-modal-dialog-focustrap?file=src/app/_modal/modal.component.html

Suresh Nagar
  • 1,021
  • 9
  • 20
  • Hey Suresh, thanks for the response. I still can't get it to work. I can see Angular injecting its focus trap anchors into the DOM but it just isn't doing anything beyond that. No matter which module I import it into, or what elements I place the directives on. Still nothing. Is it possible because I have a modal module inside of my app module, and the modal module is using `ng-content` to render the contents of each modal I call? – qbert Mar 03 '21 at 21:52
  • so long as the angular injected dom elements are enclosing the modal contents it should work. have edited the response above, to modify the modal example, have a look – Suresh Nagar Mar 04 '21 at 22:35
  • Hey Suresh, thank you so much! That works beautifully. I like your idea of setting the opened boolean. Thank you! Also, if this helps anyone else; I was able to add the A11yModule to my app.module as well and use the cdk to set the initial focus on a specific input. Works very well. – qbert Mar 05 '21 at 17:42
  • The thing that I miss here is the restoreFocus functionality when you click on the navigation input or use ctrl-L. After tabbing from the location bar you should return to the modal and not set focus on an element behind the overlay. Is there an extra directive for that? This works in the native js file https://a11y-dialog.netlify.app/ – Ron Jonk Aug 06 '21 at 09:35
  • example for setting focus from a11y dialog: after opening dialog: document.body.addEventListener('focus', this._maintainFocus, true); A11yDialog.prototype._maintainFocus = function(event) { if (this.shown && !event.target.closest('[aria-modal="true"]') && !event.target.closest('[data-a11y-dialog-ignore-focus-trap]')) { setFocusToFirstItem(this.$el); } }``` – Ron Jonk Aug 06 '21 at 22:16
0

I have created a fork with the maintainFocus function from the netlify version in it also. So when you tab from the location bar the focus is trapped again: https://stackblitz.com/edit/angular-8-custom-modal-dialog-focustrap-qlrwwr

Ron Jonk
  • 706
  • 6
  • 16