Reset form after being submitted - angularjs

I know it has been asked several times and I already read the other questions but my problem still remains. I have a simple form and I want the validation to disappear after being successfully submitted.
Here is a plunker:
function FormController($scope) {
$scope.formData = {
firstname: 'Eugene',
lastname: 'Crabs',
username: 'MrCrabs'
};
$scope.submit = submit;
function submit() {
if ($scope.someForm.$invalid) {
return;
}
$scope.formData = {};
$scope.someForm.$setUntouched();
return;
}
}
http://plnkr.co/edit/cJYStvlveHvAxhlza0Q0?p=preview
It's a simple task, it can't be that hard. What am I missing?

in your case change ng-show to
(someForm.username.$dirty || submitted)
in code
<div class="error-message" ng-show="(someForm.username.$dirty || submitted)">
note: it must be submitted, NOT form.$submitted
update your function in controller
$scope.submit = function() {
// set form is submitted
$scope.submitted = true;
if ($scope.someForm.$invalid) {
return;
}
$scope.formData = {};
// set form default state
$scope.someForm.$setPristine();
// set form is no submitted
$scope.submitted = false;
return;
}
and it works
more info about solution
plukr demo

This is a very interesting topic; I believe this forked plunker does what you want: http://plnkr.co/edit/RSSFFBmJ6MZtX3aaoNLb?p=preview
The key implementation points are:
Put an enableMessages flag in the scope
When the submit button is pressed, make the flag true before validation
Change the error message display conditions as:
<span ng-show="someForm.firstname.$error.required && (someForm.firstname.$dirty || enableMessages)">Please enter your firstname</span>
It reads "display this message if the field has an error and it has been touched by the user OR messages are enabled for the entire form".
Putting such a long condition in the HTML is a bad practice, so I would consider writing a helper function, e.g. (UNTESTED CODE):
$scope.showCondition = function(fieldName, validatorName) {
return $scope.someForm[fieldName].$error[validatorName] &&
($scope.someForm[fieldName].$dirty || enableMessages);
};
The ng-show becomes much more concise:
<span ng-show="showCondition('firstname', 'required')">Please enter your firstname</span>
In this case the enableMessages is no longer a scope variable, but a private var of the controller.

Related

How to view the ParsleyJS errors without blocking form submition

Is there a way to get the list of errors from parsley.js? I have a form that has one field that I want validate and give feedback to the user as a warning, but I don't want the error state for that field to block form submission. I am handling the form submission myself, so I'm looking for something like
$("form[name='client']").on('submit'), function(e) {
e.preventDefault();
var form = $(this);
form.parsley().validate();
// pseudo code as I don't know how to do this yet with parsley
var errors = form.parsley().errors().filter(function(err) { return err.field != field_to_ignore })
if (errors.length ) {
// error handling
} else {
// submit form
}
});
You could change the inputs or excluded options when you click on submit so that your inputs are all excluded.
My Solution is to work with two validations:
1.The first one is binding the error to the UI.
2.The second one is after adding the data-parsley-excluded=true attribute to your field_to_be_ignore.
$("#myForm").on('submit'), function(e) {
e.preventDefault();
var form = $(this);
//the first validation bind the error message to the screen
if (form.parsley().validate() == false) {
$('myFieldToIgnore').attr('data-parsley-excluded','true');
//Now let make a second validation:
form.parsley().validate();
}
else {
//submit
}
});

Toggling between $pristine and $dirty in AngularJS

I want to detect when a user has entered values into any form field by using the $dirty property and setting a flag accordingly. Not surprisingly, this works:
$scope.$watch('formDetails.$dirty', function() {
USR.userInputRecorded = true;
});
But I'd also like to detect when/if the user has emptied all fields and effectively restored the form to its original empty state. The snippet below does not work and I'm not sure why. Is there a way to watch for when the form changes back to "not dirty"?
$scope.$watch('formDetails.$pristine', function() {
USR.userInputRecorded = false;
});
Thanks.
Try this:
$scope.$watch('formDetails.$dirty', function(value) {
if (value === '') {
// field has been emptied;
your.form.$setPristine(true);
} else {
USR.userInputRecorded = true;
}
});

Enable/disable validation for angular form with nested subforms using `ng-form`

I need to enable/disable all validation rules in Angular form or subform under ng-form="myForm" based on a scope variable $scope.isValidationRequired. So, if isValidationRequired is false, none of the validations set for the designated group of fields will run, and the result will always be myForm.$valid==true, otherwise, the validation rules will run as usual.
I did a lot of research, and realized that this feature is not available out of the box with Angular. However, I found some add-ons or with some customization, it is possible.
For example, I can use the add-on angular-conditional-validation (github and demo) with custom directive enable-validation="isValidationRequired". This will be perfect, except that I cannot apply this feature for a group of fields under ng-form. I have to add this directive for each and every field where applicable.
The other solution is to use custom validation using Angular $validators pipeline. This requires some extra effort and I don't have time since the sprint is almost over and I have to give some results in a few days.
If you have any other suggestions please post an answer.
Use Case:
To clarify the need for this, I will mention the use-case. The end user can fill the form with invalid data and he can click Save button and in this case, the validation rules shouldn't be triggered. Only when the user clicks Validate and Save then the validation rules should be fired.
Solution:
See the final plunker code here.
UPDATE: as per comments below, the solution will cause the browser to hang if inner subforms are used under ng-form. More effort is needed to debug and resolver this issuer. If only one level is used, then it works fine.
UPDATE: The plunker here was updated with a more general solution. Now the code will work with a form that has sub-forms under ng-form. The function setAllInputsDirty() checks if the object is a $$parentForm to stop recursion. Also, the changeValidity() will check if the object is a form using $addControl then it will call itself to validate its child objects. So far, this function works fine, but it needs a bit of additional optimization.
One idea is to reset the errors in the digest loop if the validation flag is disabled. You can iterate through the form errors on change and set them to valid, one by one.
$scope.$watch(function() {
$scope.changeValidity();
}, true);
$scope.changeValidity = function() {
if ($scope.isValidationRequired === "false") {
for (var error in $scope.form.$error) {
while ($scope.form.$error[error]) {
$scope.form.$error[error][0].$setValidity(error, true);
}
}
}
}
Here is a plunkr: https://plnkr.co/edit/fH4vGVPa1MwljPFknYHZ
This is the updated answer that will prevent infinite loop and infinite recursion. Also, the code depends on a known root form which can be tweaked a bit to make it more general.
References: Pixelastic blog and Larry's answer
Plunker: https://plnkr.co/edit/ycPmYDSg6da10KdoNCiM?p=preview
UPDATE: code improvements to make it work for multiple errors for each field in each subform, and loop to ensure the errors are cleared on the subform level
var app = angular.module('plunker', []);
app.controller('MainCtrl', ["$scope", function($scope) {
$scope.isValidationRequired = true;
var rootForm = "form";
function setAllInputsDirty(scope) {
angular.forEach(scope, function(value, key) {
// We skip non-form and non-inputs
if (!value || value.$dirty === undefined) {
return;
}
// Recursively applying same method on all forms included in the form except the parent form
if (value.$addControl && key !== "$$parentForm") {
return setAllInputsDirty(value);
}
if (value.$validate){
value.$validate();
}
// Setting inputs to $dirty, but re-applying its content in itself
if (value.$setViewValue) {
//debugger;
return value.$setViewValue(value.$viewValue);
}
});
}
$scope.$watch(function() {
$scope.changeValidity();
}, true);
$scope.changeValidity = function(theForm) {
debugger;
//This will check if validation is truned off, it will
// clear all validation errors
if (!theForm) {
theForm = $scope[rootForm];
}
if ($scope.isValidationRequired === "false") {
for (var error in theForm.$error) {
errTypeArr = theForm.$error[error];
angular.forEach (errTypeArr, function(value, idx) {
var theObjName = value.$name;
var theObj = value;
if (theObj.$addControl) {
//This is a subform, so call the function recursively for each of the children
var isValid=false;
while (!isValid) {
$scope.changeValidity(theObj);
isValid = theObj.$valid;
}
} else {
while (theObj.$error[error]) {
theObj.$setValidity(error, true);
}
}
})
}
} else {
setAllInputsDirty($scope);
}
}
}]);

Problems with ngChange for <select> after 1.3.0 rc0

I have been using a beta version of 1.3 and now after moving to 1.3.1 I notice a problem which by checking all earlier versions I see it appears to have started in 1.3.0 rc1.
I have code like this:
<select ng-model="home.modal.topicId"
ng-change="ctrl.modalTopicChanged()"
ng-options="item.id as item.name for item in home.modal.option.topics.data"
ng-required="true">
<option style="display: none;" value="">Select Topic</option>
</select>
Prior to rc1 the ng-change was not being fired when the form was first displayed. Now it is being fired with a home.modal.topicId of undefined. This is a breaking change for me but it's not mentioned in the breaking change section and I wonder if it's a bug that has yet to be noticed.
Here is the stack trace produced:
TypeError: Cannot read property 'dataMap' of undefined
at AdminProblemController.modalTopicChanged (http://127.0.0.1:17315/Content/app/admin/controllers/ProblemController.js:109:114)
at $parseFunctionCall (http://127.0.0.1:17315/Scripts/angular.js:11387:18)
at Scope.$get.Scope.$eval (http://127.0.0.1:17315/Scripts/angular.js:13276:28)
at http://127.0.0.1:17315/Scripts/angular.js:19888:13
at http://127.0.0.1:17315/Scripts/angular.js:19499:9
at forEach (http://127.0.0.1:17315/Scripts/angular.js:331:20)
at $$writeModelToScope (http://127.0.0.1:17315/Scripts/angular.js:19497:5)
at writeToModelIfNeeded (http://127.0.0.1:17315/Scripts/angular.js:19490:14)
at http://127.0.0.1:17315/Scripts/angular.js:19484:9
at validationDone (http://127.0.0.1:17315/Scripts/angular.js:19420:9)
What I notice here is a new function: writeToModelIfNeeded
When I look at the change log differences I cannot find any mention of this function being introduced when I check all the changes and the line numbers.
I would like to get some advice on this. Firstly is it possible to find the change that caused the addition of the writeToModelIfNeeded then secondly is this the correct functionality for the select box. I thought the whole idea was that the ng-change would only fire if the model value was defined.
For reference here's the area of new code that seems to have been added with 1.3.0 rc.1
**
* #ngdoc method
* #name ngModel.NgModelController#$commitViewValue
*
* #description
* Commit a pending update to the `$modelValue`.
*
* Updates may be pending by a debounced event or because the input is waiting for a some future
* event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
* usually handles calling this in response to input events.
*/
this.$commitViewValue = function() {
var viewValue = ctrl.$viewValue;
$timeout.cancel(pendingDebounce);
// If the view value has not changed then we should just exit, except in the case where there is
// a native validator on the element. In this case the validation state may have changed even though
// the viewValue has stayed empty.
if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
return;
}
ctrl.$$lastCommittedViewValue = viewValue;
// change to dirty
if (ctrl.$pristine) {
ctrl.$dirty = true;
ctrl.$pristine = false;
$animate.removeClass($element, PRISTINE_CLASS);
$animate.addClass($element, DIRTY_CLASS);
parentForm.$setDirty();
}
this.$$parseAndValidate();
};
this.$$parseAndValidate = function() {
var parserValid = true,
viewValue = ctrl.$$lastCommittedViewValue,
modelValue = viewValue;
for(var i = 0; i < ctrl.$parsers.length; i++) {
modelValue = ctrl.$parsers[i](modelValue);
if (isUndefined(modelValue)) {
parserValid = false;
break;
}
}
if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
// ctrl.$modelValue has not been touched yet...
ctrl.$modelValue = ngModelGet();
}
var prevModelValue = ctrl.$modelValue;
var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
if (allowInvalid) {
ctrl.$modelValue = modelValue;
writeToModelIfNeeded();
}
ctrl.$$runValidators(parserValid, modelValue, viewValue, function() {
if (!allowInvalid) {
ctrl.$modelValue = ctrl.$valid ? modelValue : undefined;
writeToModelIfNeeded();
}
});
function writeToModelIfNeeded() {
if (ctrl.$modelValue !== prevModelValue) {
ctrl.$$writeModelToScope();
}
}
};
this.$$writeModelToScope = function() {
ngModelSet(ctrl.$modelValue);
forEach(ctrl.$viewChangeListeners, function(listener) {
try {
listener();
} catch(e) {
$exceptionHandler(e);
}
});
};
I was able to reproduce your issue by doing this. Without seeing your controller though not sure if same thing:
this.modal = {
topicId:null,
option:{
topics:{
data:[{id:1,name:'item1'},{id:2,name:'item2'}]
}
}
};
What is happening here is that angular says null is an invalid value so by default sets it to undefined. You can fix this by setting it to 'undefined' or adding this to your html:
ng-model-options="{allowInvalid:true}"
also tested Josep plunker and changing that value to null also caused ngChange to fire

$setPristine() does not work properly if form control has invalid data

I am trying to reset form using $setPristine().
$scope.resetDataEntryForm = function() {
$scope.dataEntryForm.$setPristine();
$scope.pr = {};
};
It works fine if all the input controls in valid state. one of the input type is URL. For example, if i have invalid URL value at the time i click reset. should the above code reset the contents of URL input field and mark error to false. I need this for validations.
To do the proper reset i have to manually reset all error flags
$scope.resetDataEntryForm = function() {
$('#dataEntryForm')[0].reset();
$scope.dataEntryForm.$setPristine();
$scope.dataEntryForm.name.$error.required = true;
$scope.dataEntryForm.site.$error.required = true;
$scope.dataEntryForm.site.$error.url = false;
$scope.pr = {};
};
Can anybody please suggest proper way to reset form using angular.js ?
I do not know if it intended or not, but $setPristine() does not change $error object.
This is the source code for setPristine on Form
form.$setPristine = function () {
element.removeClass(DIRTY_CLASS).addClass(PRISTINE_CLASS);
form.$dirty = false;
form.$pristine = true;
forEach(controls, function(control) {
control.$setPristine();
});
};
and this is the code call on Control (input element etc)
this.$setPristine = function () {
this.$dirty = false;
this.$pristine = true;
$element.removeClass(DIRTY_CLASS).addClass(PRISTINE_CLASS);
};
Seems to me that you will have to reset $error object yourself
PS: The code is taken from AngularJs version 1.2.*, you can check the current version on Github https://github.com/angular/angular.js/blob/291d7c467fba51a9cb89cbeee62202d51fe64b09/src/ng/directive/form.js

Resources