J'essaie de comprendre comment faire les choses suivantes:
Quelle est la manière acceptée de déclarer un formulaire? Je crois comprendre que vous venez de déclarer le formulaire en HTML et d’ajouter des directives de modèle ng comme ceci:
ng-model="item.name"
Ce qu'il faut envoyer au serveur. Je peux simplement envoyer l'objet item au serveur en tant que JSON et l'interpréter. Ensuite, je peux effectuer une validation sur un objet. Si cela échoue, je déclenche une erreur JSON et renvoie quoi exactement? Y a-t-il une manière acceptée de faire ceci? Comment puis-je transmettre les erreurs de validation du serveur au client de manière agréable?
J'ai vraiment besoin d'un exemple, mais les documents Angulars sont assez difficiles à comprendre.
Edit: Il semble que j'ai mal formulé ma question.
Je sais comment valider le côté client et comment traiter les erreurs/succès comme des rappels de promesses. Ce que je veux savoir, c’est le moyen accepté de regrouper les messages d’erreur côté serveur du client. Dites que j'ai un formulaire d'inscription de nom d'utilisateur et mot de passe. Je ne souhaite pas interroger le serveur à la recherche de noms d'utilisateur, puis utiliser Angular pour déterminer si un doublon existe. Je souhaite envoyer le nom d'utilisateur au serveur, valider qu'aucun autre compte n'existe avec le même nom, et ensuite soumettre le formulaire. Si une erreur survient, comment puis-je le renvoyer?
Qu'en est-il d'envoyer les données sur le serveur tel quel (clés et valeurs) avec un champ d'erreur ajouté de la manière suivante:
{
...data...
"errors": [
{
"context": null,
"message": "A detailed error message.",
"exceptionName": null
}
]
}
Puis liaison au DOM.
J'ai aussi joué avec ce genre de chose récemment et je me suis retrouvé cette démo . Je pense que ça fait ce dont tu as besoin.
Configurez votre formulaire comme d'habitude avec les validations côté client que vous souhaitez utiliser:
<div ng-controller="MyCtrl">
<form name="myForm" onsubmit="return false;">
<div>
<input type="text" placeholder="First name" name="firstName" ng-model="firstName" required="true" />
<span ng-show="myForm.firstName.$dirty && myForm.firstName.$error.required">You must enter a value here</span>
<span ng-show="myForm.firstName.$error.serverMessage">{{myForm.firstName.$error.serverMessage}}</span>
</div>
<div>
<input type="text" placeholder="Last name" name="lastName" ng-model="lastName"/>
<span ng-show="myForm.lastName.$error.serverMessage">{{myForm.lastName.$error.serverMessage}}</span>
</div>
<button ng-click="submit()">Submit</button>
</form>
</div>
Notez aussi que j'ai ajouté un serverMessage
pour chaque champ:
<span ng-show="myForm.firstName.$error.serverMessage">{{myForm.firstName.$error.serverMessage}}</span>
Ceci est un message personnalisable qui revient du serveur et fonctionne de la même manière que tout autre message d'erreur (pour autant que je sache).
Voici le contrôleur:
function MyCtrl($scope, $parse) {
var pretendThisIsOnTheServerAndCalledViaAjax = function(){
var fieldState = {firstName: 'VALID', lastName: 'VALID'};
var allowedNames = ['Bob', 'Jill', 'Murray', 'Sally'];
if (allowedNames.indexOf($scope.firstName) == -1) fieldState.firstName = 'Allowed values are: ' + allowedNames.join(',');
if ($scope.lastName == $scope.firstName) fieldState.lastName = 'Your last name must be different from your first name';
return fieldState;
};
$scope.submit = function(){
var serverResponse = pretendThisIsOnTheServerAndCalledViaAjax();
for (var fieldName in serverResponse) {
var message = serverResponse[fieldName];
var serverMessage = $parse('myForm.'+fieldName+'.$error.serverMessage');
if (message == 'VALID') {
$scope.myForm.$setValidity(fieldName, true, $scope.myForm);
serverMessage.assign($scope, undefined);
}
else {
$scope.myForm.$setValidity(fieldName, false, $scope.myForm);
serverMessage.assign($scope, serverResponse[fieldName]);
}
}
};
}
Je fais semblant d'appeler le serveur dans pretendThisIsOnTheServerAndCalledViaAjax
vous pouvez le remplacer par un appel ajax, le fait est qu'il ne fait que renvoyer l'état de validation de chaque champ. Dans ce cas simple, j'utilise la valeur VALID
pour indiquer que le champ est valide, toute autre valeur est traitée comme un message d'erreur. Vous voulez peut-être quelque chose de plus sophistiqué!
Une fois que vous avez l'état de validation du serveur, il vous suffit de mettre à jour l'état dans votre formulaire.
Vous pouvez accéder au formulaire à partir de scope. Dans ce cas, le formulaire s'appelle myForm
. Ainsi, $ scope.myForm vous le récupère. (Source pour le le contrôleur de formulaire est ici si vous voulez savoir comment ça marche).
Vous voulez ensuite indiquer au formulaire si le champ est valide/invalide:
$scope.myForm.$setValidity(fieldName, true, $scope.myForm);
ou
$scope.myForm.$setValidity(fieldName, false, $scope.myForm);
Nous devons également définir le message d'erreur. Tout d'abord, procurez-vous l'accesseur pour le champ en utilisant $ parse. Attribuez ensuite la valeur du serveur.
var serverMessage = $parse('myForm.'+fieldName+'.$error.serverMessage');
serverMessage.assign($scope, serverResponse[fieldName]);
J'espère que ça t'as aidé
J'ai la même solution que Derek, décrite sur blog codetunes . TL; DR:
affiche une erreur de la même manière que dans la solution de Derek:
<span ng-show="myForm.fieldName.$error.server">{{errors.fieldName}}</span>
ajouter une directive qui effacerait une erreur lorsque l'utilisateur modifierait l'entrée:
<input type="text" name="fieldName" ng-model="model.fieldName" server-error />
angular.module('app').directive 'serverError', ->
{
restrict: 'A'
require: '?ngModel'
link: (scope, element, attrs, ctrl) ->
element.on 'change', ->
scope.$apply ->
ctrl.$setValidity('server', true)
}
Traitez une erreur en transmettant le message d'erreur à la portée et en indiquant que le formulaire comporte une erreur:
errorCallback = (result) ->
# server will return something like:
# { errors: { name: ["Must be unique"] } }
angular.forEach result.data.errors, (errors, field) ->
# tell the form that field is invalid
$scope.form[field].$setValidity('server', false)
# keep the error messages from the server
$scope.errors[field] = errors.join(', ')
J'espère que ce serait utile :)
La réponse que Derek Ekins a donnée est très agréable à travailler. Mais: si vous désactivez le bouton d'envoi avec ng-disabled="myForm.$invalid"
- le bouton ne reviendra pas automatiquement à activé car l'état d'erreur basé sur le serveur ne semble pas avoir été modifié. Même si vous modifiez à nouveau TOUS les champs d'un formulaire pour vous conformer aux entrées valides (sur la base de la validation côté client).
Depuis juillet 2014, AngularJS 1.3 a ajouté de nouvelles fonctionnalités de validation de formulaire. Cela inclut ngMessages et asyncValidators afin que vous puissiez maintenant activer la validation côté serveur par champ avant de soumettre le formulaire.
Didacticiel de validation de formulaire 1.3 angulaire :
Références:
Par défaut, le formulaire est soumis normalement. Si vous ne fournissez pas de propriété de nom pour chaque champ du formulaire, les données correctes ne seront pas envoyées. Ce que vous pouvez faire est de capturer le formulaire avant sa soumission et de soumettre vous-même les données via ajax.
<form ng-submit="onSubmit(); return false">
Et puis dans votre $scope.onSubmit()
fonction:
$scope.onSubmit = function() {
var data = {
'name' : $scope.item.name
};
$http.post(url, data)
.success(function() {
})
.failure(function() {
});
};
Vous pouvez également valider les données en définissant les attributs requis.
Si vous choisissez ngResource, cela ressemblerait à ceci
var Item = $resource('/items/');
$scope.item = new Item();
$scope.submit = function(){
$scope.item.$save(
function(data) {
//Yahooooo :)
}, function(response) {
//oh noooo :(
//I'm not sure, but your custom json Response should be stick in response.data, just inspect the response object
}
);
};
La chose la plus importante est que votre code de réponse HTTP doit être un 4xx pour entrer le rappel d'échec.
J'avais besoin de cela dans quelques projets alors j'ai créé une directive. Enfin pris un moment pour le mettre sur GitHub pour quiconque veut une solution d'accueil.
https://github.com/webadvanced/ng-remote-validate
Caractéristiques:
Solution directe pour la validation Ajax de toute saisie de texte ou de mot de passe
Fonctionne avec la validation intégrée à Angulars et permet d’accéder à formName.inputName. $ Error.ngRemoteValidate
Limite les requêtes du serveur (400 ms par défaut) et peut être défini avec ng-remote-throttle="550"
Autorise la définition de méthode HTTP (POST par défaut) avec ng-remote-method="GET"
Exemple d'utilisation d'un formulaire de modification du mot de passe qui impose à l'utilisateur de saisir son mot de passe actuel ainsi que le nouveau mot de passe .:
<label for="newPassword">New</label>
<input type="password"
name="newPassword"
placeholder="New password"
ng-model="password.new"
required>
<label for="confirmPassword">Confirm</label>
<input ng-disabled=""
type="password"
name="confirmPassword"
placeholder="Confirm password"
ng-model="password.confirm"
ng-match="password.new"
required>
<span ng-show="changePasswordForm.confirmPassword.$error.match">
New and confirm do not match
</span>
<div>
<button type="submit"
ng-disabled="changePasswordForm.$invalid"
ng-click="changePassword(password.new, changePasswordForm);reset();">
Change password
</button>
</div>
Comme variante
// ES6 form controller class
class FormCtrl {
constructor($scope, SomeApiService) {
this.$scope = $scope;
this.someApiService = SomeApiService;
this.formData = {};
}
submit(form) {
if (form.$valid) {
this.someApiService
.save(this.formData)
.then(() => {
// handle success
// reset form
form.$setPristine();
form.$setUntouched();
// clear data
this.formData = {};
})
.catch((result) => {
// handle error
if (result.status === 400) {
this.handleServerValidationErrors(form, result.data && result.data.errors)
} else {// TODO: handle other errors}
})
}
}
handleServerValidationErrors(form, errors) {
// form field to model map
// add fields with input name different from name in model
// example: <input type="text" name="bCategory" ng-model="user.categoryId"/>
var map = {
categoryId: 'bCategory',
// other
};
if (errors && errors.length) {
// handle form fields errors separately
angular.forEach(errors, (error) => {
let formFieldName = map[error.field] || error.field;
let formField = form[formFieldName];
let formFieldWatcher;
if (formField) {
// tell the form that field is invalid
formField.$setValidity('server', false);
// waits for any changes on the input
// and when they happen it invalidates the server error.
formFieldWatcher = this.$scope.$watch(() => formField.$viewValue, (newValue, oldValue) => {
if (newValue === oldValue) {
return;
}
// clean up the server error
formField.$setValidity('server', true);
// clean up form field watcher
if (formFieldWatcher) {
formFieldWatcher();
formFieldWatcher = null;
}
});
}
});
} else {
// TODO: handle form validation
alert('Invalid form data');
}
}