zeldi
zeldi

Reputation: 4903

How to move focus on form elements the Angular way

Objective

My current code

form.component.html

<form>
      <input type="text" class="form-control" id="el1" name="el1"
             required focus
             [(ngModel)]="el1" #el1
             (keydown)="processEl1KeyDown($event)"
      >
      <input type="text" class="form-control" id="el2" name="el2"
             required
             [(ngModel)]="el2" #el2
             (keydown)="processEl2KeyDown($event)"
      >
</form>

form.component.ts

@Component({
    selector: 'my-form',
    templateUrl: 'app/form.component.html'
})
export class FormComponent {

    constructor(private renderer:Renderer) {
    }

    el1: string;
    el2: string

    @ViewChild("el1")
    el1El: ElementRef;

    @ViewChild("el2")
    el2El: ElementRef;


    @HostListener('document:keydown', ['$event'])
    handleKeyboardEvent(event: KeyboardEvent) {
        if (!this.el1) {
            this.setFocus(this.el1El);
        } else {
            this.setFocus(this.el2El);
        }
    }


    processEl1KeyDown(event: KeyboardEvent) {
        event.stopPropagation();
        if (event.keyCode == 13) {
            this.setFocus(this.el2El);
        }
    }


    processEl2KeyDown(event: KeyboardEvent) {
        event.stopPropagation();
        if (event.keyCode == 13) {
            this.submitForm();
        }
    }

    submitForm() {
        console.log("submit form");
    }

    setFocus(element: ElementRef) {
        this.renderer.invokeElementMethod(element.nativeElement, 'focus');
    }
}

Questions

  1. How can I get template variable name of a focused element (eg #el1, #el2) from within FormComponent class? document.activeElement returns DOM element.
  2. Can I get Angular2 object from a DOM? something like ElementRef.create('<div></div>')
  3. If I want to pass arbitrary string to my setFocus('el1') function, how can I build ElementRef from that? I can use @ViewChild decorator, but I need something which can be defined during runtime? like new ViewChild('el1')
  4. What would be more "Angular2" way of solving my problem? With a BehaviorSubject as mentioned in this answer - Delegation: EventEmitter or Observable in Angular2?

Edit

Maybe questions are not clear enough. To sum up: If I have n input elements (el1, el2 ... eln), how do I know in module which element has focus (maybe via template variable) and then move focus to a different element (via a string, which corresponds to another template variable again). I think I am looking at angular.element equivalent - Get ComponentRef from DOM element, but I believe this might not be the right way.

Upvotes: 3

Views: 4917

Answers (1)

hholtij
hholtij

Reputation: 2936

I think a focus directive might solve most of the problems you listed. I had similar issues and have written this directive to solve them:

@Directive
({
    selector: '[hasFocus]'
})

export class HasFocusDirective
{
    @Input("hasFocus") hasFocus: boolean;
    constructor(private elementRef: ElementRef) {}

    ngAfterViewInit()
    {
        if (this.hasFocus)
        {
            this.elementRef.nativeElement.focus();
        }
    }

    ngOnChanges(changes: SimpleChanges)
    {
        if (changes["hasFocus"] && changes["hasFocus"].currentValue === true)
        {
            this.elementRef.nativeElement.focus();
        }
    }
}

[hasFocus] is a boolean and you can use any boolean expression to dynamically control focus of elements that use it.

Upvotes: 1

Related Questions