J'ai un formulaire avec un champ qui agit comme une saisie semi-automatique. Si l'utilisateur entre un mot et appuie sur entrer, le contenu du champ doit être ajouté à une liste située sous le champ.
Le problème: lorsque l'utilisateur tape, tout le formulaire est bien sûr soumis.
J'ai déjà return false
sur la fonction qui gère la frappe. Mais le formulaire semble être soumis avant même que cette fonction ne soit appelée.
Comment puis-je empêcher que cela se produise?
La forme de base:
<div id="profileForm">
<form [formGroup]="profileForm" (ngSubmit)="onSubmit()" method="post" *ngIf="!showSuccessMessage">
<div class="row">
<div class="form-group col-xs-12 col-sm-6">
<label for="first_name">My Skills</label>
<div class="autocomplete">
<input formControlName="skill_string" [(ngModel)]="skillString" name="skill_string"
type="text" class="form-control" id="skill_string" placeholder="Comma separated" (keyup.enter)="skillsHandleEnter(skillString)">
<ul class="autocomplete-list" *ngIf="skillHints.length > 0">
<li class="list-item" *ngFor="let skill of skillHints" (click)="addSkillFromAutocomplete(skill)">{{skill}}</li>
</ul>
</div>
<div id="skill-cloud" class="tag-cloud">
<span class="skill-tag tag label label-success" *ngFor="let skill of selectedSkills" (click)="removeSkill(skill)">{{skill}} x</span>
</div>
</div>
</div>
<div class="row">
<hr>
<div class="form-group submit-group">
<div class="col-sm-12">
<button type="submit" class="btn btn-primary pull-right" [disabled]="!profileForm.valid">Save</button>
</div>
</div>
</div>
</form>
</div>
Le composant de base (j'ai enlevé beaucoup de logique pour l'afficher ici):
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { Subscription } from 'rxjs/Rx';
import 'rxjs/add/operator/debounceTime';
import * as _ from 'lodash';
import { MemberService } from '../shared/index';
@Component({
moduleId: module.id,
selector: 'signup',
templateUrl: 'signup.component.html',
styleUrls: ['signup.component.css']
})
export class SignupComponent implements OnInit {
private profileForm:FormGroup;
private validation_errors:Array<any>;
private selectedSkills:Array<string>;
private skillHints:Array<string>;
private skillString:string;
constructor(private route: ActivatedRoute,
private formBuilder: FormBuilder,
private memberService: MemberService,
private router: Router ) {
this.selectedSkills = [];
this.skillHints = [];
this.skillString = '';
// Set up form
this.profileForm = this.formBuilder.group({
skill_string: ['']
});
}
ngOnInit(): any {
// Do something
}
updateSelectedSkills(skillString:string):void {
if(skillString) ) {
let cleanString = skillString.trim().replace(/[ ]{2,}/g, ' ');
this.selectedSkills = _.compact(this.selectedSkills.concat(cleanString.split(',')));
this.skillString = '';
this.skillHints = [];
}
}
skillsHandleEnter(skillString:string):void {
console.log("ENTER");
this.updateSelectedSkills(skillString);
return false;
}
autocompleteSkills(term:string):void {
this.memberService.autocompleteSkills(term).subscribe(
res => {
this.skillHints = [];
for(let i = 0; i < res.data.length; i++) {
this.skillHints.Push(res.data[i].name);
}
}
);
}
addSkillFromAutocomplete(skillString:string):void {
this.selectedSkills.Push(skillString);
this.memberProfile.skill_string = '';
this.skillHints = [];
this.skillString = '';
}
onSubmit():void {
this.memberService.saveProfile(this.memberProfile, this.selectedSkills).subscribe(
res => {
console.log(res);
}
);
}
}
Essayer
<form (keydown.enter)="$event.target.tagName == 'TEXTAREA'" [formGroup]="profileForm" (ngSubmit)="onSubmit($event)">
Cela permettra également à enter
dans Textarea
s.
La réponse était donc plutôt simple… Ce n'était pas Event.preventDefault()
puisque j'écoutais Enter sur le champ de saisie et non le bouton. Supprimer type="submit"
de la button
n'était pas suffisant, car tous les boutons sont de type submit par défaut. La seule modification nécessaire concernait l'élément button, en ajoutant type="button"
explicitement et en ajoutant un écouteur (click)
:
<button type="button" (click)="onSubmit()" class="btn btn-primary pull-right" [disabled]="!profileForm.valid">Save</button>
Le seul type de problème: Soumettre le formulaire avec enter ne fonctionne jamais. Serait un petit peu plus élégant pour empêcher seulement enter de soumettre le formulaire lorsque le focus est dans le champ de saisie semi-automatique.
Modifier:
Pour empêcher uniquement enter de soumettre le formulaire lorsque le curseur est dans le champ de saisie semi-automatique, utilisez la solution d'Ankit Singh et modifiez-la un peu:
<form [formGroup]="profileForm" (ngSubmit)="onSubmit()" method="post" (keydown.enter)="$event.target.id != 'skill_string'" *ngIf="!showSuccessMessage">
(Remarque: la condition doit renvoyer false pour empêcher le déclenchement de l'action par défaut.)
Bien sûr, nous avons à nouveau besoin de notre bouton d'envoi régulier (sans l'événement click joint, sinon le formulaire sera envoyé deux fois):
<button type="submit" class="btn btn-primary pull-right" [disabled]="!profileForm.valid">Save</button>
Vous pouvez également vérifier le event.target.classList
si vous souhaitez utiliser une classe .autocomplete
. Ou déplacez la logique de contrôle vers une fonction dans laquelle vous transmettez le $event
.
Les événements dans Angular 2 se comportent comme des événements DOM normaux. Pour capturer l'objet événement, transmettez $event
en tant que paramètre dans le rappel d'événement à partir du modèle:
Html:
<button (keyup.enter)="skillsHandleEnter($event, skillString)"></button>
JavaScript utilisant Event.preventDefault () :
@Component(...)
class MyComponent {
skillsHandleEnter(event, skillString) {
event.preventDefault();
// ... your logic
}
}
Empêcher un formulaire de soumettre le enter ou en cliquant sur button
name __
<textarea (keydown.enter)="provoked($event)"></textarea>
<button (keydown.enter)="provoked($event)" (click)="provoked($event)"></button>
provoked($event) {
$event.preventDefault()
}
au cas où vous auriez besoin de transmettre des données à la méthode, essayez
<textarea (keydown.enter)="provoked($event, data)"></textarea>
<button (keydown.enter)="provoked($event, data)" (click)="provoked($event, data)"></button>
provoked($event, data) {
$event.preventDefault()
// process the data here
}