I've got an issue with the Angular validation classes. I've got three classes in my css with three different images, that shows if the field is required, if the user has filled in correct or not. E.g. I have one input field where the user types in a personal number, and I look it up in a service and fills the model with firstname, lastname, address etc. which also populates the input fields.
I made a simple example
HTML
<div ng-app="ebu" ng-controller="search">
<button id="search-apparentice" ng-click="addName()">Search apprentice</button>
<hr>
<input id="username" type="text" ng-model="user.name" required>
</div>
JS
angular.module('ebu', []).
controller('search', function($scope) {
$scope.user = {};
$scope.addName = function()
{
$scope.user.name = "Kasper";
}
})
CSS
input[required].ng-invalid
{
border: 1px solid red;
}
input[required].ng-valid
{
border: 1px solid green;
}
input[required].ng-pristine
{
border: 1px solid blue;
}
[FiddleJS][1][1]: http://jsfiddle.net/kaspergantzhorn/z1wg6tko/9/
It's still pristine because pristine means it has never had focus. The user is not clicking on it, therefore it never gets dirty. If this isn't a user-modified field, pristine is not the right thing to use.
Additionally, in the ng-change of the input, you should be able to use $setDirty.
<input type="text" ng-change="formName.inputName.$setDirty()" />
For you code above:
<div ng-app="ebu" ng-controller="search">
<button id="search-apparentice" ng-click="addName(); myForm.name.$setDirty()">Search apprentice</button>
<hr>
<form name="myForm">
<input name="name" id="username" type="text" ng-model="user.name" required>
</form>
</div>
Related
Target
To make Cypress recognise and manipulate a DatePicker element.
Problem
I cannot make Cypress recognise the data-testid of a DatePicker element, and so have no way of testing it E2E.
Error
Timed out retrying: Expected to find element: [data-testid="edit_dob"], but never found it.
Attempts
I have tried placing the DatePicker element inside input and div elements to target instead, but they fail as expected for other reasons.
I have also looked through the docs for both DatePicker and Cypress for mention of the other, and for similar questions here.
Code
The code below works as expected, it's just Cypress that seems to be incompatible with the DatePicker input.
function displayEditDetails() {
let listOfForms = [];
for (const detail in details) {
listOfForms.push(
<FormGroup controlId={detail} key={`key_edit_${detail}`}>
<FormLabel>{detail.charAt(0).toUpperCase() + detail.slice(1).replace('_', ' ')}</FormLabel>
<FormControl
data-testid={`edit_${detail}`}
type={detail}
value={details[detail] || ''}
onChange={handleDetailsChange}
/>
</FormGroup>
)
}
listOfForms.push(
<DatePicker
data-testid={'edit_dob'}
selected={dob}
onChange={setDob}
maxDate={new Date()}
/>
);
return(
listOfForms
)
}
Test
it('Displays the edit fields', () => {
cy.get('[data-testid="accountButton"]')
.click();
cy.get('[data-testid="editUserDetailsButton"]')
.click();
cy.get('[data-testid="user_email"]')
.should('not.be.visible');
cy.get('[data-testid="edit_full_name"]')
.should('be.visible');
cy.get('[data-testid="edit_dob"]')
.should('be.visible');
cy.get('[data-testid="edit_email"]')
.should('be.visible')
.should('have.value', 'test#editdetails.com');
})
Once I can target it, I plan to test clearing the current input and typing a new date in. Any help targeting the picker and warnings of further potential pitfalls are appreciated.
Edit: Here's the generated HTML of the form.
<form>
<div class="form-group">
<label class="form-label" for="email">Email</label>
<input data-testid="edit_email" type="email" id="email" class="form-control" value="admin#example.com" style="background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABHklEQVQ4EaVTO26DQBD1ohQWaS2lg9JybZ+AK7hNwx2oIoVf4UPQ0Lj1FdKktevIpel8AKNUkDcWMxpgSaIEaTVv3sx7uztiTdu2s/98DywOw3Dued4Who/M2aIx5lZV1aEsy0+qiwHELyi+Ytl0PQ69SxAxkWIA4RMRTdNsKE59juMcuZd6xIAFeZ6fGCdJ8kY4y7KAuTRNGd7jyEBXsdOPE3a0QGPsniOnnYMO67LgSQN9T41F2QGrQRRFCwyzoIF2qyBuKKbcOgPXdVeY9rMWgNsjf9ccYesJhk3f5dYT1HX9gR0LLQR30TnjkUEcx2uIuS4RnI+aj6sJR0AM8AaumPaM/rRehyWhXqbFAA9kh3/8/NvHxAYGAsZ/il8IalkCLBfNVAAAAABJRU5ErkJggg=="); background-repeat: no-repeat; background-attachment: scroll; background-size: 16px 18px; background-position: 98% 50%; cursor: auto;">
</div>
<div class="form-group">
<label class="form-label" for="full_name">Full name</label>
<input data-testid="edit_full_name" type="full_name" id="full_name" class="form-control" value="Jareth">
</div>
<div class="form-group">
<label class="form-label" for="address">Address</label>
<input data-testid="edit_address" type="address" id="address" class="form-control" value="">
</div>
<div class="form-group">
<label class="form-label" for="postcode">Postcode</label>
<input data-testid="edit_postcode" type="postcode" id="postcode" class="form-control" value="">
</div>
<div data-testid="edit_dob">
<div class="react-datepicker-wrapper">
<div class="react-datepicker__input-container">
<input type="text" class="" value="04/03/1999">
</div>
</div>
</div>
<button data-testid="confirmChangesButton" type="submit" class="LoaderButton btn btn-primary btn-block">Confirm Changes</button></form>
The problem is visible when looking at the HTML generated by running the app.
DatePicker HTML
<div data-testid="edit_dob">
<div class="react-datepicker-wrapper">
<div class="react-datepicker__input-container">
<input type="text" class="" value="04/03/1999">
</div>
</div>
</div>
The DatePicker is rendered as an input element inside a few divs, and the test id doesn't target the input.
Solution
The Selector Playground API for Cypress suggests cy.get('.react-datepicker__input-container > input'), the class of the parent div of the input. With this I can target the DatePicker, clear it, and type new input.
Notes For Next Time
Examine the HTML
Read the Docs more thoroughly
Reference the concepts used in the cy.pickDateRange command in the Cypress Real World App. It is a payment application to demonstrate real-world usage of Cypress testing methods, patterns, and workflows.
In the code for the command, you will find it is necessary to examine the HTML produced and traverse it appropriately for your needs.
Here is my HTML input tag on which I have created conditional ng-class
<input type="text" class="form-control" placeholder="label" name="label"
ng-required="true" ng-class="{ missing: $ctrl.flag=='true'}"
ng-model="$ctrl.DataToSend.label[$ctrl.language]" />
And here is my CSS
.missing {border: solid 1px red;}
When my $ctrl.flag is true ng-class should get applied but it's not happening. Why?
You have to put the class which you want to apply in quotation marks. Otherwise it looks for an AngularJS variable where the string for the class name is contained.
Also make sure your condition is not always false. In your case the $ctrl.flag variable might store a boolean value. So change your condition to: $ctrl.flag
Updated Code:
<input type="text" class="form-control" placeholder="label" name = "label" ng-required="true" ng-class="{ 'missing': $ctrl.flag }" ng-model = "$ctrl.DataToSend.label[$ctrl.language]"/>
You can try this snippet.
Jsfiddle link
Snippet:
var app = angular.module('myApp', []);
app.controller('ctrl', function($scope) {
$scope.flag = true;
$scope.toggleClass = function() {
$scope.flag = !$scope.flag;
};
});
.missing {border: solid 1px red;}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='myApp'>
<div ng-controller='ctrl'>
{{flag}}
<input type='text' class='form-control' ng-model='test' ng-class="{'missing': flag}" />
<button ng-click='toggleClass()'>
Change
</button>
</div>
</div>
This will work
I found the Issue with my code there is some other CSS which is overriding to my 'missing' css when changed the overridden CSS its started working fine.
input[type="text"].missing {border: 1px solid #ff0000;}
Previously it was
input[type="text"] {border: 1px solid #ddd;}
I'm new to AngularJS and needing help on how to toggle classes on form elements if the input is valid or invalid.
I have noted that most validation is done within the DOM - see here: https://scotch.io/tutorials/angularjs-form-validation
However, I'm wanting to avoid using the dom to handle validation. I want to use a custom directive to handle if a input is valid or invalid based upon validation rules.
Here is my HTML / Angular markup so far:
<fieldset ng-class="{error: loginForm.username.$invalid}">
<div class="form-input-error" ng-show="loginForm.$error">
Username is too short.
</div>
<div class="vfnz-fieldWrapper">
<input
type="text"
id="username"
ng-model="username"
class="vfnz-input--text"
placeholder="Username"
ng-minlength="3"
ng-maxlength="8" />
<label for="name" class="vfnz-input-label">
Username
</label>
</div>
</fieldset>
Here is a JSFIDDLE of my code so far
Basically when a input is invalid - add class to the fieldset. If the input is valid add class to fieldset.
from angular 1.3.x they came up with ng-messages ,i think you should try this. The advantages of using it is it will show the appropiate error if your input contains multiple error's.Add your class to the validation div
the below example checks for required and number .
<form name='cliForm'>
<input ng-model="vm.formData.newCreditLimit" type="text" name="changeCreditLimit" ng-pattern="/^[0-9]{1,15}$/" required />
<div class="highlight" ng-messages="cliForm.changeCreditLimit.$error" ng-if="cliForm.$submitted">
<div ng-message="required">* This field is required!</div>
<div ng-message="pattern">* Not a valid number!</div>
</div>
</form>
before using this include the ng-messages script since its in different module and inject the ng-messages to your angular module
I'm also looking for a solution to this issue, but the (lazy) answer I'm opting for right now is just to let some directives determine whether the element is valid or invalid, and just let angular handle the css classes. What I'm doing is to just add the ff to my css file:
.ng-invalid {
border-color: #e51c23;
color: #e51c23 !important;
}
.ng-error {
border-color: #e51c23;
color: #e51c23 !important;
}
.ng-valid {
border-color: #4CAF50;
color: #4CAF50 !important;
}
I have implemented parsley on a complicated form that uses on-blur validation.
I have run into an issue where i go through the form, the fields are validated one by one, then based on a dropdown change, I have to destroy the Parlsey object in order to modify the validation and the fields that were valid are no longer valid.
How can I retain the already validated fields.
For example say I have the following:
<input type="text" id="1">
<input type="text" id="2">
<select id="select">
<option id=1>1</option>
<option id=2>2</option>
<option id=3>3</option>
</select>
<input type="text" id="3">
<input type="text" id="4">
<input type="text" id="5">
###Scenario:
I enter data in ID=1 and ID=2 textboxes, success class is added as I tab out of the textbox.
I change select box to option 2
I destroy parsley
Success class is removed from ID=1 and ID=2 textboxes
Currently I am doing the following:
$("#form").parsley().destroy();
$('#form').parsley({
successClass: "valid",
errorClass: "invalid",
errorsWrapper: '<div></div>',
errorTemplate: '<p></p>'
}).validate('section');
###What this does
This revalidates all the fields
###What I am looking for
I need ID=1 and ID=2 to remain valid after I destroy.
When you call destroy() all things from Parsley are destroyed. This means that all the classes, messages (DOM), objects and events will be destroyed.
If you're looking for a way to maintain the UI aspect, you can do that with a not-so-pretty solution:
In your styles, where you have .valid { ...} add another class: .valid, .fake-valid { ... }. Do the same for invalid.
Before calling destroy(), navigate through all the fields and check if there is a class valid or invalid
If so, apply a class called fake-valid or fake-invalid.
Using the event parsley:form:init, loop through the fields again and change their classes from fake-... to the correct classes.
Parsley will validate the fields but the UI is maintained.
Check this JsFiddle.
<style>
.valid, .fake-valid {
color: #468847;
background-color: #DFF0D8;
border: 1px solid #D6E9C6;
}
.invalid, .fake-invalid {
color: #B94A48;
background-color: #F2DEDE;
border: 1px solid #EED3D7;
}
</style>
<form id="myForm" method="post">
<input type="text" id="1" data-parsley-trigger="focusout" required />
<input type="text" id="2" data-parsley-trigger="focusout" required />
<select id="select">
<option id=1>1</option>
<option id=2>2</option>
<option id=3>3</option>
</select>
</form>
<script>
$(document).ready(function() {
var parsleyOpts = {
successClass: "valid",
errorClass: "invalid",
errorsWrapper: '<div></div>',
errorTemplate: '<p></p>'
};
var ParsleyForm = $("#myForm").parsley(parsleyOpts);
$("#select").on('change', function() {
// before destroy, add fake class
for (var i in ParsleyForm.fields) {
var elem = ParsleyForm.fields[i].$element;
if (elem.hasClass('valid'))
elem.addClass('fake-valid');
else if(elem.hasClass('invalid'))
elem.addClass('fake-invalid');
}
ParsleyForm.destroy();
ParsleyForm = $("#myForm").parsley(parsleyOpts);
});
// when parlsey is initialized, lets see if the fields have fake classes
// if so, add parsley classes
$.listen('parsley:form:init', function(formInstance) {
for (var i in formInstance.fields) {
var elem = formInstance.fields[i].$element;
if (elem.hasClass('fake-valid'))
elem.toggleClass('fake-valid valid')
if (elem.hasClass('fake-invalid'))
elem.toggleClass('fake-invalid invalid');
}
});
});
</script>
I'm trying to create a subform <div ng-form="vacancyForm"> with Angular.js
There is a type of data that has numerous fields
Headline
Date available
Price
All have required validation on them.
Once I submit that data I'll do what I need with it but I want to reset the subform so that all the fields are not dirty and the form is valid as at the moment clearing out the fields work but all fields are invalid as they are now dirty, but empty marking them as invalid.
An example field
<div class="control-group" ng-class="getErrorClasses(vacancyForm.headline)">
<label class="control-label" for="headline">Headline</label>
<div class="controls">
<input type="text" class="input-xlarge" id="headline" name="headline" required ng-model="new_vacancy.headline">
<span class="help-inline" ng-show="showError(vacancyForm.headline, 'required')">This field is required</span>
</div>
</div>
Here is the function that is called when submitted
$scope.addVacancy = function(){
// save the submitted data
$scope.school.vacancies.push($scope.new_vacancy);
// now clear it out
$scope.new_vacancy = {};
$scope.new_vacancy.date = new Date();
// this clears out all the fields and makes them all invalid
// as they are empty. how to reset the form???
}
Set the name attribute on the subform and then you can $scope.formName.$setPristine(); where formName is what the name attribute is. An element is no longer pristine when the value has been changed.
http://docs.angularjs.org/api/ng.directive:form.FormController#$setPristine
Update
The above answer was solely for 1.2, but in 1.3 angular introduced the concept of a "touched" input. Now when an element is blurred angular will mark the field as touched. Similar to $setPristine, you can set the input back by using $scope.formName.$setUntouched().
https://docs.angularjs.org/api/ng/type/form.FormController#$setUntouched
touched vs pristine: touched means the field has been blurred while pristine means the field's value has been modified. Angular's docs note that "Setting a form controls back to their untouched state is often useful when setting the form back to its pristine state."
Edit
Here is a fiddle demo: https://jsfiddle.net/TheSharpieOne/a30kdtmo/
angular.module('myApp', [])
.controller('myCtrl', myCtrl);
function myCtrl() {
var vm = this;
vm.reset = function() {
vm.myForm.$setPristine();
vm.myForm.$setUntouched();
vm.email = vm.password = '';
}
}
.ng-invalid.ng-touched {
outline: 2px solid blue;
}
.ng-invalid.ng-dirty {
outline: 2px solid red;
}
.ng-invalid.ng-dirty.ng-untouched {
outline: 2px solid green;
}
form,
form div {
padding: 5px 10px;
}
h3,
h4 {
margin-bottom: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl as ctrl">
<form name="ctrl.myForm">
<div>
<label for="email">Email</label>
<input name="myInput" type="email" ng-model="ctrl.email" id="email" required>
</div>
<div>
<label for="password">Password</label>
<input name="myPassword" type="password" minlength="8" ng-model="ctrl.password" id="password" required>
</div>
<div>
<button ng-click="ctrl.reset()" type="button">Reset</button>
</div>
</form>
<div>
<h4>Form Level</h4>
<div>$dirty: {{ctrl.myForm.$dirty}}</div>
<div>$pristine: {{ctrl.myForm.$pristine}}</div>
<h4>Input Level</h4>
<h5>Email Input</h5>
<div>$dirty: {{ctrl.myForm.myInput.$dirty}}</div>
<div>$pristine: {{ctrl.myForm.myInput.$pristine}}</div>
<div>$touched: {{ctrl.myForm.myInput.$touched}}</div>
<h5>Password Input</h5>
<div>$dirty: {{ctrl.myForm.myPassword.$dirty}}</div>
<div>$pristine: {{ctrl.myForm.myPassword.$pristine}}</div>
<div>$touched: {{ctrl.myForm.myPassword.$touched}}</div>
</div>
<div>
<h3>Color outlines for input</h3>
<div title="The form loads this way, it can still be invalid since required fields are empty to start with">untouched, pristine: no outline</div>
<div title="Such as in the middle of typing a valid email for the first time">invalid, untouched, dirty: green outline</div>
<div title="blurred with invalid input">invalid, touched, dirty: red outline</div>
<div title="focued and blurred without typing">invalid, touched: blue outline</div>
</div>
</div>