I'm using Angular $http (http://docs.angularjs.org/api/ng.$http) to submit a form. The server returns JSON:
[{
"hasValidationErrors": true,
"validationErrors": {
"inputNameAttributeValue": "Error Message 1",
"anotehrInputNameAttributeValue": "Error Message 2"
}
}]
Once I receive the JSON response, how do I notify Angular that certain form fields are in an error state so that it automatically changes the view to show error messages?
For example, if I use this markup (from http://www.ng-newsletter.com/posts/validations.html):
<div class="row">
<div class="large-12 columns">
<label>Your name</label>
<input type="text"
placeholder="Name"
name="inputNameAttributeValue"
ng-model="signup.name"
ng-minlength=3
ng-maxlength=20 required />
<div class="error"
ng-show="signup_form.name.$dirty && signup_form.name.$invalid">
<small class="error"
ng-show="signup_form.name.$error.required">
Your name is required.
</small>
<small class="error"
ng-show="signup_form.name.$error.minlength">
Your name is required to be at least 3 characters
</small>
<small class="error"
ng-show="signup_form.name.$error.maxlength">
Your name cannot be longer than 20 characters
</small>
</div>
</div>
</div>
I'm looking for a solution that works with my existing data binding markup in my html doc.
I can change the markup, but I don't want to have two separate sections of markup for each form input (one for client side validation and another for validation after the JSON response).
Update:
For example, if required form fields have not been touched by the user
before the form is submitted, they will not show error messages. So,
my goal is that upon form submission, all form fields are validated
and show validation messages if needed.
That's because ng-show is triggered by both $dirty and $invalid flag.
You can manually set all $dirty of input fields to true when ngSubmit fired.
But, in the case the user has tampered with the form, the JSON
response from the server should be used to add validation messages as
well.
If validation failed on server, just bring these tampered values back to angular models on client and use $apply if needed. AngularJS should do the rest of work.
Simulating example: http://jsfiddle.net/djpV8/4/
Origin:
how about set error messages to the scope and simply display it when it's defined:
scope.errorMessages = returnedJson.validationErrors;
just add a block for displaying custom message from server
<!-- place error messages next to corresponding input fields. -->
<small class="error"
ng-show="errorMessages.inputName"
ng-bind="errorMessages.inputName"></small>
Related
I'm working in a recovery password form with Spring Boot and Thymeleaf, the process is that the backend sends a link to the user with a token, this token is saved in a DB with the user information. The user clicks the link and the backend validate the token, if it's correct it redirects with the user information to the "new pass" form where the user has to introduce a new password and repeat it. When the user introduces the passwords and press save, it sends the information to the back end, with the userId it searches the register and changes the password.
In the controller where I validate the token when the user presses the link, I search the users' information and I build and usersRecoverPassDto with the userId and the passwords null and organize the model object to send it to the "new pass" form like this:
PasswordManagmentPostDto passManagment = new PasswordManagmentPostDto();
passManagment.setUserId(userId);
model.addAttribute ("passInfo", passManagment);
In the "new pass" form I received the correct information:
passInfo={
userId: 123456,
newPassword: null,
repeatePassword: null
}
The "new pass" form I code is like this:
<form action="#" th:action="#{/password/recovery}" th:object="${passInfo}" method="post">
<div class="mb-2 mr-sm-2 mb-sm-0 position-reNew pass:</label>
<input type="password" th:field="*{newPassword}" placeholder="ContraseƱa" class="form-control">
</div>
<div class="mb-2 mr-sm-2 mb-sm-0 position-relative form-group">
<label for="repeatePass" class="mr-sm-2">Repeat pass:</label>
<input type="password" th:field="*{repeatNewPassword}" placeholder="Repita contraseƱa" class="form-control"/>
</div>
<div class="mb-2 mr-sm-2 mb-sm-0 mt-3 position-relative form-group">
<input name="submit" type="submit" value="Save" class="inputButton btn btn-success "/>
</div>
</form>
I enter the two passwords and press the button Save but in the controller, I receive the next object:
passInfo={
userId: null,
newPassword: password,
repeatePassword: password
}
The userId parameter is just a parameter that passes without any modification and without being shown but I don't know how to persist this value from the controller to the view and then to the other controller.
I trayed adding another input to the new pass form:
<input type="text" th:field="*{userId}" th:value="*{userId}" placeholder="userId" class="form-control"/>
And I did nothing with this input, just put the two passwords and press the save button and it worked, I received the object with the correct information.
Of course, the userId parameter must not be shown in the form or modified.
Can someone help me, please?
Thank you very much in advance
OLD ANSWER
Use a hidden input:
<input type="hidden" id="userId" name="userId" th:value="*{userId}" />
This is part of what hidden inputs are for, to maintain data that should be sent back to the controller, but isn't something for the user to see / change.
NEW ANSWER
Per our discussion in the comments, it sounds like the appropriate approach is to retain the user's token on the front end. Pass that token to the controller when submitting the form, then look up the user by that token to reset the password.
Using Thymeleaf + Spring and form validation, I want to display errors that are related to a field next to this field.
<input type="text"
th:field="*{companyName}" class="form-control"
placeholder="Code client" th:errorClass="'error'"/>
If the field has an error, the class "error" is applied indeed. But is there an easy way to display also the validation error of the field?
Use this code to display error:
<p class="alert alert-danger" th:if="${#fields.hasErrors('companyName')}" th:errors="*{companyName}"></p>
I'm trying to find the best way in Angular to invalidate a form not due to a specific element, but due to a system-level error AFTER submission via AJAX. For example, you could put in a valid email and password (both good strings), press submit, and find out there is a system error that should trigger a generic error message on the form. Since this isn't tied to anything in the data model, what is the best way I can generically call the form 'invalid'?
<form name="loginForm" class="loginForm" ng-submit="loginSubmit(loginForm)">
<div class="form-group">
<label for="exampleInputEmail1">Email address</label>
<input type="email" name="email" class="form-control" id="exampleInputEmail1" placeholder="Enter email" ng-model="login.email" required>
<span class="error" ng-show="loginSubmit.email.$error">Required!</span><br>
</div>
<div class="form-group">
<label for="exampleInputPassword1">Password</label>
<input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password" ng-model="login.password" minlength="8" required>
</div>
<button type="submit" class="btn btn-default">Log In</button>
</form>
and...
ngModule.controller('LoginController', function($scope, $location) {
$scope['login'] = {};
$scope['loginSubmit'] = function(form) {
var loginPromise = myAsyncLoginFuncYouCanAssumeWorks();
loginPromise.done(function(){
$location.path('/');
});
loginPromise.fail(function() {
//how best to trigger a generic error in the form here?
});
};
});
As you can see, I'd like to trigger some form-wide error state after submission. It really could be as simple as adding an invalid form class to the form, but again, I'd like to know the purest Angular way to do this.
Add a label to your form with your generic error which shows upon a scope variable being true when an error occurs:
<div class="alert alert-danger" role="alert" ng-show="loginError">There was an error with your login details. Please check them and try again</div>
then when your promise fails:
loginPromise.fail(function () {
$scope.loginError = true;
});
maybe also could be nice if you have many system messages to abstract them all out into a separate service so you can inject the systemmessages service into your controller and then simply bind:
<div class="alert alert-danger" role="alert" ng-show="loginError">{{ systemMessages.loginError }}</div>
Alternatively as you use Bootstrap maybe inject the $modal service and show the error message inside a popup.
It is also important to make sure you try to use a bearer token stored in localatorage as oppose to cookies for persistence, so it doesn't get sent to the server on each request.
Anti forgery token would also be very beneficial for SPAs.
Your server could return some sort or error pay load with error key or error code.
{errors:[{key:"invalid.password"}]}
Assign the error response to your scope:
loginPromise.fail(function(response) {
$scope.errors = response.data;
});
Next, add filter to translate error key/code into error messages:
angular.module('mtApp').filter('errorFilter', function() {
return function(e) {
if (e === 'invalid.password') {
return 'Invalid password, please try again.';
}
};
});
Finally, display the appropriated errors as a list:
<div ng-show="errors">
<ul>
<li ng-repeat="e in errors">{{e.key | errorFilter}}</li>
</ul>
</div>
Optionally, you can reuse this same "$scope.erros" object combined to ng-class and control the CSS of each field with error.
I'm using codeception to write acceptance tests for a laravel application. The application is a simple CRUD app, and my tests just test filling out and submitting the application's various forms. I have a working test which creates a new resource but then when I try to edit that same resource I get the following error:
Here's what the (working) test looks like for creating the resource:
$I = new AcceptanceTester($scenario);
$I->wantTo('Create Additional');
$I->login($I, 'username', 'password');
$I->click('Additional');
$I->seeInCurrentUrl('/admin/additional');
$I->click('Add Additional');
$I->seeInCurrentUrl('admin/additional/create');
$I->see('Add Additional');
//Fill out form fields
$I->fillField('name', 'Test Name');
$I->fillField('anchor', 'Additional Services in Cusco');
$I->selectOption('publish', '1');
$I->click('Save');
$I->click('Additional');
$I->see('Additional Test Name');
$I->cleanup($I);
The test for editing the resource is almost identical:
$I = new AcceptanceTester($scenario);
$I->wantTo('Edit Additional');
$I->login($I, 'username', 'password');
$I->click('Additional');
$I->seeInCurrentUrl('/admin/additional');
$I->click('Edit');
$I->seeInCurrentUrl('admin/additional/edit/1');
$I->see('Editing');
// Fill out form fields
$I->fillField('name', 'Additional Name Edited');
$I->fillField('anchor', 'Edited anchor');
$I->selectOption('publish', '1');
$I->click('Save');
$I->click('Additional');
$I->see('Additional Name Edited');
$I->cleanup($I);
Yet when I run the edit test I get the following error:
1) Failed to edit additional in AdditionalEditCept
Couldn't fill field "name","Additional Name Edited":
InvalidArgumentException: Unreachable field "name"
That seems straightforward enough--the "name" field must not be there. But when I look at the source name="name" is there just like it is for the additional/create view. I should point out that codeception didn't detect the field with
$I->see('name');
either. Here's what the relevant code looks like in AdditionalEditCept.fail.html
<div class="form-group">
<label for="name">Additional Name:</label>
<input class="form-control" placeholder="" autocomplete="off" name="name" type="text" value="Edited Additional Services in Cusco" id="name">
</div>
It seems really simple; codeception needs to find html elements by CSS selectors, I give them a CSS selector. What could be going on here?
A bit late on this but I've had a similar error if my html isn't valid eg
<div class="form-group">
<label for="name">Additional Name:</label>
<input name="name" type="text">
</div>
</div>
The Grails 2.0.4 documentation for validation shows you how to display error messages at the top of the page and how to add a css class to an element if a field is invalid, but it doesn't tell you how to display the error message next to the fields themselves, something like this:
-----------------------
Name: | | You must enter a name!
-----------------------
How do you retrieve the specific error message for an invalid field and then display it next to the field it's for?
Actually, the documentation does show how to do this, it just isn't overly clear that this is what they mean:
<g:renderErrors bean="${book}" as="list" field="title"/>
If you specify the field attribute, it will only render error(s) for that field. So then it is just up to you to write the HTML accordingly.
<input type="text" ... /> <g:if test="${bean.hasErrors}"><g:renderErrors bean="${book}" as="list" field="title"/></g:if>
It can get as simple or as complicated as you would like it and while I generally like grails plugins, this just seems simple enough to do without one and you have more control over the markup.
I use the Grails Fields plugin to do this, and it works a treat.
It makes it easy to create default templates for form field rendering. For example I have the following in grails-app/views/_fields/default/_field.gsp:
<%# page defaultCodec="html" %>
<div class="control-group${invalid ? ' error' : ''}">
<label class="control-label" for="${property}${index ?: ""}">${label}</label>
<div class="controls">
<%= widget.replace("id=\"${property}\"", "id=\"${property}${index ?: ""}\"") %>
<g:if test="${invalid}"><span class="help-inline">${errors.join('<br>')}</span></g:if>
</div>
</div>
As you can see from the HTML the errors are displayed inline. Here is part of my login form:
<g:form controller="home" action="login" >
<f:field bean="user" property="email"/>
<f:field bean="user" property="password">
<g:field type="password" name="${property}" value="${value}"/>
</f:field>
</g:form>
Here is the custom error in context, wrapped around username field. This will do what you want.
<dt>User Id</dt>
<dd><g:textField name="username" value="${user?.username}"/>
<g:hasErrors bean="${user}" field="username">
<g:eachError bean="${user}" field="username">
<p style="color: red;"><g:message error="${it}"/></p>
</g:eachError>
</g:hasErrors>
</dd>
I would recommend going with Jquery validation plugin. There's several Grails plugin about this, but they are a bit out-dated. Besides, I think this task is pretty simple for using another plugin.