Victoria
Victoria

Reputation: 187

How to set focus to the nearest sibling (if exist) or parent element in angular 12

I have been struggling to find a solution to my task - when I remove attachment from the list of attachment object belonging to the comment, I want to set the focus to the nearest attachment left in the array - if the array is blank, I want to set the focus to the parent comment component

Child Edit-comment html file

<div *ngFor="let attachment of comment?.attachments; let index = index">
   <h4>{{ attachment.filename }}</h4>
   <Button [attr.id]="'remove-btn-' + index" (click)="deleteAttachment(attachment)">Remove attachment</Button>
</div>

Child Edit-comment ts file

export class EditCommentComponent {
  @Input() comment: Comment;
  @Output() deleteCommentAttachment = new EventEmitter<CommentAttachment>();

  public deleteAttachment(attachment: CommentAttachment): void {
    this.deleteCommentAttachment.emit(attachment);
}

Parent list-comments html file

<app-edit-comment
  [comment]="comment"
  (deleteCommentAttachment)="deleteCommentAttachment($event)"
></app-edit-comment>

Parent list-comments ts file

export class ListCommentsComponent {
   public comment: Comment;
   // validationProtocolP - this is a variable holding all the mock data for 
                            comments

   public deleteCommentAttachment(attachment: CommentAttachment): void {
   // logic for deleting comment attachment
   // logic to set focus -
   const attachmentIndex = this.validationProtocolP.comments[commentIndex].attachments.findIndex((at) => at.id === this.attachment.id);

   if (this.comment.attachments[attachmentIndex - 1]) {
          setTimeout(() =>
            this.setFocus(document.getElementById('remove-att-btn-' + (attachmentIndex - 1))),
          );
        } else if (this.comment.attachments[attachmentIndex + 1]) {
          setTimeout(() =>
            this.setFocus(document.getElementById('remove-att-btn-' + attachmentIndex)),
          );
        } else {
          console.log('Focus set to parent of attachments');
        }


  public setFocus(el: HTMLElement): void {
    const focusElement = (el.previousSibling as HTMLElement) || (el.nextSibling as HTMLElement);
    if (focusElement) {
      focusElement.focus();
    }
  }
}

Upvotes: 0

Views: 1619

Answers (1)

hansmaad
hansmaad

Reputation: 18905

You can pass references to DOM elements and use nextSibling or previousSibling to focus these elements.

This is an example of the basic principle (https://stackblitz.com/edit/angular-ivy-wkwgsl?file=src/app/app.component.ts). You can adapt to your specific logic:

@Component({
  selector: 'my-child',
  template: `<div><button (click)="remove.emit()">{{ comment }}</button></div>`,
})
export class ChildComponent {
  @Input() comment: string;
  @Output() remove = new EventEmitter();
}

@Component({
  selector: 'my-app',
  template: `
  <div #parent>
    <div #child *ngFor="let c of children; let index = index">
      <my-child [comment]="c" (remove)="remove(index, child, parent)"></my-child>
    </div>
  </div>`,
})
export class AppComponent {
  children = ['1', '2', '3'];

  remove(index: number, child: HTMLElement, parent: HTMLElement) {
    this.children.splice(index, 1);
    const focusElement =
      (child.previousSibling as HTMLElement) ||
      (child.nextSibling as HTMLElement) ||
      parent;
    if (focusElement && typeof focusElement.focus === 'function') {
      focusElement.focus();
    }
  }
}

Upvotes: 1

Related Questions