问题
I'm trying to simulate tab key press when enter is pressed on input control.For that I use a directive:
private el: ElementRef;
@Input() onEnter: string;
constructor(private _el: ElementRef, public renderer: Renderer) {
this.el = this._el;
}
@HostListener('keydown', ['$event']) onKeyDown(e: any) {
if ((e.which === 13 || e.keyCode === 13)) {
e.preventDefault();
const event = new KeyboardEvent("keypress", {
"key": "Tab"
});
this.el.nativeElement.dispatchEvent(event);
............
code for enter key fires but no tab is sent
回答1:
Updated see better aproach in this stackblitz(sorry,I don't remember the post where I talk about it)
If you want to use ENTER to focus an element you can use a directive
@Directive({
selector: '[next-tab]',
})
export class NextTabDirective {
@Input('next-tab') nextControl: any;
@HostListener("keydown.enter", ["$event"])
onEnter(event: KeyboardEvent) {
if (this.nextControl) {
if (this.nextControl.focus) {
this.nextControl.focus();
this.nextControl.select();
event.preventDefault();
return false;
}
}
}
constructor(private control: NgControl) {
}
}
You can use in a form like
<form (submit)="Submit()">
<input #input0 [next-tab]="input1" />
<input #input1 [next-tab]="input2" />
<!--the last not have [next-tab]-->
<!-an ENTER make a submit -->
<input #input2 />
<button type="button" (click)="cancel()">Cancel</button>
<button type="submit">OK</button>
</form>
I would like not use this ugly work-around, but we can improve the directive sending as next-tab an array of controls
@Directive({
selector: '[next-tab]',
})
export class NextTabDirective {
@Input('next-tab') nextControl: any[]; //<--an array of controls
@HostListener("keydown.enter", ["$event"])
onEnter(event: KeyboardEvent) {
//find the nextControl not disabled. We check if c is defined
//This allow us to use *ngIf and not put the control
let nextControl=this.nextControl.find(c=>c && !c.disabled);
if (nextControl) {
if (nextControl.focus) {
nextControl.focus();
nextControl.select();
event.preventDefault();
return false;
}
}
}
constructor(private control: NgControl) {
}
}
The form is look like
<form (submit)="Submit()">
<!--see that we create an array-->
<input #input0 [next-tab]="[input1,input2,input3]" />
<input #input1 [next-tab]="[input2,input3]" />
<!--if only one element, we make an array of one element-->
<input #input2 [style.display]="existInput2?'inherit':'none'" [next-tab]="[input3]" />
<!--if we want make invisible control NOT use *nfIf, therefore, we must hidden and disabled too -->
<input #input3 />
<button type="button" (click)="cancel()">Cancel</button>
<button type="submit">OK</button>
</form>
Finally I put a stackblitz in https://stackblitz.com/edit/angular-v8fkkf
回答2:
There are a more "confortable way to do a "next-tab" th idea is that the next-tab directive has two variables "self" and "next-control". We not use @Input, the directive is like
@Directive({
selector: '[next-tab]',
})
export class NextTabDirective {
self:any;
nextControl:any; //See that is not a @Input
@HostListener("keydown.enter", ["$event"])
onEnter(event: KeyboardEvent) {
if (this.nextControl) {
if (this.nextControl.focus) {
this.nextControl.focus();
this.nextControl.select();
event.preventDefault();
return false;
}
}
}
constructor(private control: ElementRef) {
//we store in "self" the native element. This make us easy refered to
//html properties of control
this.self=control.nativeElement;
}
}
In the .html see that the directive is simply next-tab, not [next-tab] and not [next-tab]="". Take account that input4 is too "decorated" with the directive, and that all the input have a "name" html property
<form [formGroup]="myForm" (submit)="submit(myForm)">
<p>
<input type="checkbox" formControlName="ckDisabled"/>Disabled input 2
</p>
<p>
<input type="checkbox" formControlName="ckInvisible"/>Invisible input 3
</p>
<p>
Input 1: <input name="input1" formControlName="input1"
next-tab/>
</p>
<p>
Input 2: <input name="input2" formControlName="input2" tabindex=""
[enableControl]="!myForm.controls['ckDisabled'].value"
next-tab/>
</p>
<p [style.display]="myForm.controls['ckInvisible'].value?'none':'inherit'">
Input 3: <input name="input3" formControlName="input3"
[enableControl]="!myForm.controls['ckInvisible'].value"
next-tab
/>
</p>
<p>
Input 4: <input name="input4" next-tab formControlName="input4"/>
</p>
<p>
<button>OK</button>
</p>
</form>
And now the .ts. We use ViewChildren to get all the element that have a next-tab directive.
export class AppComponent implements OnInit,AfterViewInit {
//We use a ViewChildren to get all the controls width NextTabDirective
//We use QueryList<NextTabDirective>. So the elements of "inputs"
//will have the properties: self and nextControl
@ViewChildren(NextTabDirective) inputs: QueryList<NextTabDirective>;
myForm:FormGroup;
constructor(private fb:FormBuilder){}
//in ngAfterViewInit we asing to nextControl, the self of the next control
ngAfterViewInit() {
const controls=this.inputs.toArray();
//controls will be [input1,input2,input3,input4]
for (let i=0;i<controls.length-1;i++)
controls[i].nextControl=controls[i+1].self; }
}
ngOnInit()
{
this.myForm=this.fb.group({
ckDisabled:false,
ckInvisible:false,
input1:'',
input2:'',
input3:'',
input4:''
});
this.onChanges();
}
onChanges()
{
this.myForm.valueChanges.subscribe((value)=>{
if (this.inputs)
{
//see how we get the control using self.name
const input1=this.inputs.find(c=>c.self.name=="input1");
const input2=this.inputs.find(c=>c.self.name=="input2");
const input3=this.inputs.find(c=>c.self.name=="input3");
const input4=this.inputs.find(c=>c.self.name=="input4");
input1.nextControl=(value.ckDisabled && value.ckInvisible)?input4.self:
(value.ckDisabled)?input3.self:input2.self;
input2.nextControl=(value.ckInvisible)?input4.self:input3.self;
}
})
}
submit(form:any)
{
if (form.valid)
alert("Fom submitted!!")
}
}
there are a stackBlitz in https://stackblitz.com/edit/angular-dzdxmh
来源:https://stackoverflow.com/questions/50932956/angular-5-simulate-keypress-on-input-controls