AngularJS: on-click if pristine OR if dirty and valid - javascript

I have an input field:
<input name="fName" type="text" class="form-control dude.firstName"
ng-trim="false"
ng-pattern="patterns.name"
ng-model="dude.firstName"
ng-model-options="{ updateOn: 'blur' }"
required>
<span class="error" ng-show="idForm.fName.$error.pattern">
Please only use letters, forward slashes, and hyphens
</span>
My requirements are this:
If user has not changed anything, it needs to run saveIdentification
If user has changed something and it is not valid, then stop and allow the form to display the message
If the user has changed something and it is valid, then run saveIdentification
<span
ng-show="localEditing.id=='SAVE'"
tabindex="0"
title="Save Changes"
class="globalIcon-save action-edit-button"
ng-click="(idForm.$pristine || (idForm.$dirty && idForm.$valid)) && saveIdentification()">
</span>
The solution above fulfills requirements 1 and 2 but not 3. It will not save if the form is changed and valid.

Please see below snippet. I just made a slight variation to your code in order to receive the form object as a parameter in the saveIdentification function.
Notice that I added an input with ng-maxlength="3" in order to reproduce the scenario where the form is invalid (When the input text is larger than 3)
When the conditions described by you are met a "ran saveIdentification!" text is logged in the browser console (devtools).
angular
.module('app', [])
.controller('myCtrl', function() {
var vm = this;
vm.wizard = {
saveIdentification: fnSaveIdentification
};
return vm.wizard;
function fnSaveIdentification(form) {debugger;
if (form && (form.$pristine || form.$valid)) { //<-- Condition here!
console.log('ran saveIdentification!');
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="myCtrl as ctrl">
<form name="myForm">
<input name="i1" type="text" ng-maxlength="3" ng-model="ctrl.input">
Submit
<!-- Display message here!! (modify as you need it)-->
<span ng-show="myForm.$invalid">Form is invalid</span>
</form>
</div>

try this:
ng-disabled="(idForm.$dirty && idForm.$invalid))"
ng-click="saveIdentification()"
>
<span ng-show="(idForm.$dirty && idForm.$invalid))">
Your error message goes here!!!
</span>

Related

Check if form input fields have not changed

I have a form, let's call it myForm similar to this:
<form name="myForm" class="vertical grid-block shrink" ng-init="initSearch()" ng-submit="doSearch(myForm.$valid)" novalidate>
And three input fields: one with keeps details, second which is a starting date and third which is a ending date.
The fields look like this:
<input id="details" type="text" ng-model="myObject.details" placeholder="DETAILS_PLACEHOLDER">
<input id="from" class="uppercase" type="text" name="from" placeholder="{{datePlaceHolder}}" ng-model="myObject.from" required/>
<input id="until" class="uppercase" type="text" name="until" placeholder="{{datePlaceHolder}}" ng-model="myObject.until"/>
And I want to display a button based on the form fields. If the form fields have not changed the button should be hidden.
I tried using $dirty but the problem is $dirty remains true even if the user types in details and then deletes the text.
Anyone has any solution for this?
Also the solution must work even if I come back to the form from another page which probably has another controller.
Any help is appreciated. Thank you
This should fix that. It checks for $pristine, ie untouched.
<button ng-show="myForm.$pristine"></button>
Take a look at sample below, basically you need $viewValue for displaying/hiding submit button.
var app = angular.module('app', []);
app.controller('ctrl', function($scope) {
$scope.myObject = {};
$scope.datePlaceHolder = 'from date';
$scope.datePlaceHolder2 = 'until date';
$scope.doSearch = function() {
alert('submit');
}
$scope.initSearch = function() {
console.log('init search called');
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.0/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<p>Need to fill FROM and UNTIL values to submit</p>
<form name="myForm" ng-init="initSearch()" ng-submit="doSearch(myForm.$valid)" novalidate>
<input id="details" type="text" ng-model="myObject.details" placeholder="DETAILS_PLACEHOLDER">
<input id="from" type="text" name="from" placeholder="{{datePlaceHolder}}" ng-model="myObject.from" required/>
<input id="until" type="text" name="until" placeholder="{{datePlaceHolder2}}" ng-model="myObject.until" />
<button type="submit" ng-show="myForm.from.$viewValue && myForm.until.$viewValue">Submit</button>
</form>
</div>
You can add more conditions to
ng-show="myForm.from.$viewValue && myForm.until.$viewValue"
Notice that if you delete value from input, submit button disappears

Can't submit form with data-live-search as true (Bootstrap-select)

I'm making a issue reporting form with categories and sub categories. The form also requires the user to add their e-mail address (from a list). The list has the data-live-search = true to give them the possibility to search for it in the list.
The form submits if you first start to type in the search field for the e-mail and then selects it, but if you don't write in the search bar and just select it from the list (without typing anything), the form won't submit... This is what the form looks like (a simplified version, but still not working):
<form role="form" name="form" id="form" action="file.php" method="post">
<!-- AUTHOR NAME -->
<div class="form-group" id="form-group-author-name">
<label class="sr-only" for="author-name">Author's name</label>
<input type="text" name="author-name" id="form-author-name" placeholder="Author's name" class="form-control" required>
</div>
<!-- AUTHOR E-MAIL -->
<div class="form-group" id="form-group-author-email">
<select class="selectpicker" name="author-email" id="form-author-email" data-width="100%" title="Author's e-mail" data-live-search="true" required>
<option>email1#example.com</option>
<option>email2#example.com</option>
<option>email3#example.com</option>
<option>email4#example.com</option>
<option>email5#example.com</option>
</select>
<p class="help-block">Search for your e-mail.</p>
</div>
<button type="submit" class="btn">Submit</button>
</form>
I've also tried to add an event listener to the select field and use $("#" + "form-author-email").selectpicker('refresh'); and document.getElementById("form-author-email").value = $("#" + "form-author-email").val(); in case it was because of the field not updateing, but that wasn't working either.
If I use console.log($("#" + "form-author-email").val()); I still get the value selected, so I don't understand what the problem is.
I've figured out the problem. I've added a jQuery validation function (for all input fields) that used e.preventDefault(); and added a error styling-class if the input was empty, which it would always be if they didn't type anything into the search bar. I've add the code below in case anybody has done the same thing:
jQuery(document).ready(function() {
$('.form input[type="text"]').on('focus', function() {
$(this).removeClass('error');
});
$('.form').on('submit', function(e) {
$(this).find('input[type="text"]').each(function(){
if( $(this).val() == "" ) {
e.preventDefault();
$(this).addClass('error');
}
else {
$(this).removeClass('error');
}
});
});
});

Validate input type="number" before submitting

I'm running into an interesting problem while trying to restrict user input to a number.
My HTML looks like so:
<input name="activeDate" type="number" class="form-control" ng-model="account.tag.activeDate"></input>
...and my relevant Angular controller code like so(this is inside a click handler):
tag.activeDate = moment.utc().add(tag.activeDate, tag.tagDurationType).toISOString();
Unfortunately when I click the button which submits my form and calls that click handler I get this error in my console:
[ngModel:numfmt] Expected `2015-08-27T22:09:18.919Z` to be a number
Looks like my input is checked upon submitting, when it's converted to a date within my controller. How do I limit user input to numbers without running into this?
Use ng-pattern="/^(\d)+$/" - it's simple regex expression
var app = angular.module('num', []);
app.controller('numCtrl', function($scope, $http){
$scope.digits = {};
});
angular.bootstrap(document.body, ['num']);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-controller="numCtrl">
<form class="digits" name="digits" ng-submit="getGrades()" novalidate >
<input type="text" placeholder="digits here plz" name="nums" ng-model="nums" required ng-pattern="/^(\d)+$/" />
<p class="alert" ng-show="digits.nums.$error.pattern">Numbers only, please.</p>
<br>
<input class="btn" type="submit" value="Do it!" ng-disabled="!digits.$valid" />
</form>
</body>
Select your input element, then do the following you have to look up value of number0Key and number9Key:
myElement.on("keypress", function(e) {
if (e.which < number0Key || e.which > number9Key) {
e.stopPropagation();
}
});

$myform.textinput.$isvalid is true when it should not

The whole form should not be valid until stuff[] has at least one item added to it. When the user enters in a value into the text box and then clicks add it then adds the value to stuff[], only at this point when a value has been added to stuff[] should the submit button be enabled. However, as soon as the user types anything into the text box without clicking add, and thus nothing in stuff[], it makes the form valid and enables the submit button.
<form ng-app="myApp" ng-controller="myCtrl" name="myForm" ng-submit="submit()" novalidate>
<div ng-repeat="things in stuff">
<table><tr><td>{{things}}</td></tr></table>
</div>
<input type="text" name="app" ng-model="input" ng-required="!stuff[0]" />
<button ng-disabled="!input" ng-click="add()">
<span> add</span>
</button>
<input type="submit" value="Submit" ng-disabled="myForm.$invalid" />
<script>
angular.module('myApp', []).controller('myCtrl', function ($scope) {
$scope.stuff = [];
$scope.input = null;
$scope.add = function () {
var l = $scope.stuff.length;
$scope.stuff[l] = $scope.input;
$scope.input = null;
};
$scope.submit = function () {};
});
</script>
</form>
[EDIT]
To answer your question directly: !stuff[0] is TRUE when there is nothing in the array, and FALSE otherwise. When it is TRUE, then input is required, making the form 'initially' invalid. As soon as you type something into the input, then the requirement is now fulfilled, meaning that the form is valid, and you can now click the submit button. The condition actually has nothing to do with actually putting something into the array.
This is fixable by attaching a condition to stuff.length as proposed by my answer below. It won't make the form invalid (which you can easily do with this condition elsewhere) but it will at least disable the submit button.
[/EDIT]
I don't understand why you have ng-required there, as you are wanting to disable the submit button, meaning the logic should be attached to the submit button, not the input text box.
I would do this instead:
<input type="text" name="app" ng-model="input"/>
<button ng-disabled="!input" ng-click="add()">
<span> add</span>
</button>
<input type="submit" value="Submit" ng-disabled="stuff.length == 0" />
Which will disable the submit button if there is nothing in stuff.

Form validation summary in AngularJS with custom message per field

I'm trying to render a validation summary on a page using AngularJS. Here's what I have so far:
<div ng-app>
<div ng-controller="ctrl">
<form name="userForm">
<fieldset>
<legend>User Info</legend>
<p><label>Name: <input type="text" required ng-maxlength="15" name="name" ng-model="name" /></label></p>
<p><label>Age: <input type="number" required name="age" ng-model="age" /></label></p>
<p><label>Favorite Color: <input type="text" required name="favColor" ng-model="favColor" /></label></p>
<p><input type="button" value="Submit" ng-click="submitForm()" /></p>
</fieldset>
</form>
<div id="validationSummary" ng-show="hasValidationErrors()">
<ul>
<li ng-repeat="error in validationErrors">{{ error }}</li>
</ul>
</div>
</div>
</div>
In my controller, I'm creating an array with all of the errors.
function ctrl($scope) {
$scope.hasValidationErrors = function () {
return $scope.validationErrors && $scope.validationErrors.length > 0;
};
$scope.submitForm = function() {
$scope.validationErrors = [];
for (var property in $scope.userForm) {
if ($scope.userForm.hasOwnProperty(property) && $scope.userForm[property].$invalid) {
$scope.validationErrors.push($scope.userForm[property].$name);
}
}
}
}
The thing I can't figure out is: how can I get more than just the name of each field that is invalid? I've noticed that there is also an $error property on each field. Outputting this instead of $name gives me the following:
{"required":true,"maxlength":false}
{"required":true,"number":false}
{"required":true}
So I can get the field name, and I can get an object that describes what is wrong with that particular field. How can I define an error message, so that if a field is required it will output "{name} is required"? It seems like this could be a data- attribute on the input element itself, although I don't know how I would access that attribute.
Of course, it's also possible that I'm making things entirely too difficult on myself. Is there a better way to approach this while staying in the "AngularJS" world?
Here's a link to the jsFiddle I've been working on.
A far easier and cleaner way is demonstrated here
Simply put (where form1 is your form name attribute):
<ul>
<li ng-repeat="(key, errors) in form1.$error track by $index"> <strong>{{ key }}</strong> errors
<ul>
<li ng-repeat="e in errors">{{ e.$name }} has an error: <strong>{{ key }}</strong>.</li>
</ul>
</li>
</ul>
A totally dynamic validation summary based on AngularJS 1.5.7 with ngMessages using field names that the user recognizes
A template with error messages:
<script type="text/ng-template" id="error-messages">
<span ng-message="required">This field is required.</span>
<span ng-message="email">Please enter a valid email.</span>
</script>
Display of the error summary (here for a form named "candidateForm"):
<div data-ng-if="candidateForm.$submitted && candidateForm.$invalid">
Please correct these fields and then try to send again:
<ul>
<li data-ng-repeat="field in candidateForm" data-ng-if="candidateForm[field.$name].$invalid">
<div>
{{ getValFieldName(field) }}
<span data-ng-messages="candidateForm[field.$name].$error" role="alert">
<span data-ng-messages-include="error-messages"></span>
</span>
</div>
</li>
</ul>
</div>
A helper function to get the name of the label associated with the input field (instead of displaying input field names or "internal ID codes" to users):
$scope.getValFieldName = function (field) {
return $("label[for=" + field.$name + "]").text(); // to get label associated with input field
// return $("#" + field.$name).attr("placeholder"); // to get placeholder of input field
};
You can reuse this set of standard error messages on multiple forms, ngMessages ensure only one error displayed per field, and looks like the fields are listed in the order they appear in the HTML.
Should probably be made into a directive instead of the jQuery-style helper function, and might even want to add a click-handler on each error message to scroll to the input field with the error. Maybe another will run with that idea?
Use below line to get values of every text box
var value = $scope.userForm[property].$name;
var vl =$('*[name="' + value + '"]').data('required-message')
$scope.validationErrors.push(vl);

Categories

Resources