I have created an angular form that displays input validation errors received from the server. My solution works fine except for one small issue.
If I submit the form with no value, after the page loads, I am receiving the correct response from my server i.e. 422, but the validation error is not displayed. If I then start typing a value in the input the validation error flashes and disappears.
I am almost certain that it has something to do with my directive, but I'm not sure how to fix it. This is my current directive code:
var appServices = angular.module('webFrontendApp.directives', []);
appServices.directive('serverError', function(){
return {
restrict: 'A',
require: '?ngModel',
link: function(scope,element,attrs,ctrl){
element.on('change keyup', function(){
scope.$apply(function(){
ctrl.$setValidity('server', true);
});
});
}
};
});
I think the issue is with the element.on('change keyup'.... section of this code. That's why the error message flashes when I start typing. Also when I change this to 'change' instead of 'change keyup', the error message is displayed permanently when I start typing.
Does anybody have an idea of how I can display the error message even if I did not type any value into the input before submitting it the first time?
UPDATE AS PER COMMENT
Here is my form:
<form ng-submit="create(memberData)" name="form" novalidate>
<div class = "row form-group" ng-class = "{ 'has-error' : form.email.$dirty && form.email.$invalid }">
<input type="text" ng-model="memberData.email" placeholder="janedoe#mail.com" name="email" class="col-xs-12 form-control" server-error>
<span class="errors" ng-show="form.email.$dirty && form.email.$invalid">
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
<span ng-show="form.email.$error.server">{{errors.email}}</span>
</span>
</div>
<div class="row">
<button type="submit" class="btn btn-danger col-xs-12">Join Private Beta</button>
</div>
</form>
And my controller:
$scope.memberData = {};
$scope.create = function() {
var error, success;
$scope.errors = {};
success = function() {
$scope.memberData = {};
};
error = function(result) {
angular.forEach(result.data.errors, function(errors, field) {
$scope.form[field].$setValidity('server', false);
$scope.errors[field] = errors.join(', ');
});
};
BetaMember.save({ beta_member: { email: $scope.memberData.email || "" }}).$promise.then(success, error);
};
Since the form itself doesn't have a $setValidity method, because doens't have an ng-model, and assuming that the server error is not referred to a single field ( in this case a $setValidity method is preferred ), I think that the simplest solution could be this one:
Create a form with some random validation ( this one simply needs at least the username ) and a div that can display a custom server error.
<div ng-controller="AppCtrl">
<form novalidate name="createForm">
<div class="inputWrap">
<input ng-model="name" name="name" type="text" required placeholder="John Doe">
<span ng-if="createForm.name.$dirty && createForm.name.$invalid">
Some kind of error!
</span>
</div>
<div ng-if="serverError">
{{ serverError.message }}
</div>
<input
value="Join Private Beta"
ng-disabled="createForm.$invalid || serverError"
ng-click="create()"
type="button">
</form>
</div>
Then in your controller you can add the create method that deal with YourService ( should return a promise ) and if the response is failure you can create a simple object with a custom server error message inside which will also be usefull to disable the form button, if you need.
var AppCtrl = function($scope, YourService){
// Properties
// Shared Properties
// Methods
function initCtrl(){}
// Shared Methods
$scope.create = function(){
YourService.makeCall().then(function(response){
// Success!
// Reset the custom error
$scope.serverError = null;
}, function(error){
// Do your http call and then if there's an error
// create the serverError object with a message
$scope.serverError = {
message : 'Some error message'
};
})
};
// Events
// Init controller
initCtrl();
};
AppCtrl.$inject = [
'$scope',
'YourService'
];
app.controller('AppCtrl', AppCtrl);
I mean it's very simple snippet here, I just wanted to bring an example. Nothing to complex, but you can scale this to something more.
It seems that there was an extremely simple workaround. Since I had the filter form.email.$dirty in my view, the error would not be displayed if the user clicked submit without clicking on the form first.
After removing form.email.$dirty and only having form.email.$invalid, it works perfectly. I think this is sufficient in my case, as this validation is dependant on a server response and will not be triggered before the form is submitted. The error object is also cleared up in my controller ensuring that the page does not load an error when it's first loaded.
Related
I know there is other similar questions to this, but i've read them all and none of them is working for me.
When i try to set my form to a pristine, I keep getting this error:
TypeError: Cannot read property '$setPristine' of undefined
The controller and my angular version (1.4.2) are everything ok, because i also have other thing happening within the same function calling the $setPristine(); method and that one is working.
This is the code i'm using:
html
<form name="cadTel" novalidate>
<div class="form_group">
<label class="col-md-4 f--label"><i class="fa fa-asterisk"></i>Nome</label>
<div class="col-md-8 f--input">
<input type="text" name="ds_contato" ng-model="tel.ds_contato" ng-required="true" />
</div>
</div>
<div class="form_group">
<label class="col-md-4 f--label"><i class="fa fa-asterisk"></i>Telefone</label>
<div class="col-md-8 f--input">
<input type="text" name="num_tel" mask="(99) 9?9999-9999" ng-model="tel.num_tel" ng-required="true" />
</div>
</div>
<input type="button" class="bt-m bt-suc" name="cadastrar" value="Salvar" ng-click="add_telefone(tel)">
<div class="bt-m bt-war" ng-click="reset()">Limpar</div>
</form>
app.js
$scope.tel = {};
$scope.add_telefone = function(tel) {
$scope.tel = angular.copy(tel);
$http({
method: 'POST',
url:'dist/php/db.php?action=add_telefone',
data:$scope.tel,
})
.success(function (data, status, headers, config) {
$scope.reset();
})
.error(function (data, status, headers, config) {
});
};
$scope.reset = function() {
$scope.tel = {};
$scope.cadTel.$setPristine();
};
The option to clean the values are working but to set pristine none.
Any ideas?
I faced the same issue and below fix corrected it.
Angular is not aware of the form id. Please change the form name as below
form name="form.cadTel"
Also during the controller startup set the form
$scope.form = {};
Check this plnkr link.
I am using bootstrap modal to show the forms and when I click Cancel or click outside of modal I wanted to reset the form. I spent hours troubleshooting AngularJS form Cannot read property '$setPristine' of undefined error and solved it by adding a simple condition before reseting the form:
$('#myform-modal').on('hidden.bs.modal', function () {
resetModal();
});
resetModal = function() {
if ( $scope.MyForm ) {
$scope.MyForm.$setPristine();
$scope.MyForm.$setUntouched();
$scope.MyForm.$submitted = false;
delete $scope.data;
}
}
I have a form like this -
<form name="myForm" novalidate>
There are some fields in the form which I am validating and then submitting the form like this -
<input type="button" ng-click="Save(data)" value="Save">
In the controller, I want to check if the form is not valid then Save() should show some error on the page. For that, I am setting up a watch like this -
$scope.$watch('myForm.$valid', function(validity) {
if(validity == false)
// show errors
});
But I am always getting this error on running it -
Cannot read property '$valid' of undefined
Can someone explain why?
Thanks
You just misspelled "myForm" in your controller code. In order to remove the error, Write "myform" instead of "myForm".
However I expect what you want is like this.
$scope.Save = function(data){
alert($scope.myform.$valid);
}
I setup jsfiddle.
In my case I was wrapping the form in a modal created in the controller and therefore got the same error. I fixed it with:
HTML
<form name="form.editAddress" ng-submit="save()">
<div class="form-group">
<label for="street">Street</label>
<input name="street" type="text" class="form-control" id="street" placeholder="Street..." ng-model="Address.Street" required ng-minlength="2" />
<div class="error" ng-show="form.editAddress.street.$invalid">
<!-- errors... -->
</div>
</div>
<button type="submit" class="btn btn-primary" >Save address</button>
</form>
JS
angular.module("app").controller("addressController", function ($scope, $uibModal, service) {
$scope.Address = {};
$scope.form = {};
$scope.save = function() {
if (modalInstance !== null) {
if (isValidForm()) {
modalInstance.close($scope.Address);
}
}
};
var isValidForm = function () {
return $scope.form.editAddress.$valid;
}
});
I'm trying to add a single disabled="disabled" to a button, to block the element while the it is creating a server requests, however, for some reason the documentations information is not working for me, so my case is:
HTML:
<button ng-disabled="isDisable" ng-click="submit()">Submit</button>
JS (into my controller):
$scope.isDisable = false;
$scope.submit = function() {
$scope.isDisable = true;
//$http request....
something.sucess(function(data) {
$scope.isDisable = false;
};
};
for some reason my logic is not working.
Any ideas?
You don't need the {{}} as it's not an expression.
<button ng-disabled="isDisable" ng-click="submit()">Submit</button>
EDIT
If this is a form then on the form tag do ..
<form name="aForm" novalidate ng-submit="submit()">
then submit button
<button type="submit" ng-disabled="isDisable">Submit</button>
I am trying to show a Validation Summary, a Div on top of the page with all the Validation error messages in angularjs, on form submit.
I am using the below logic to show/ hide the top div with validation messages,
<div class="alert alert-error" ng-show="submitted && myForm.$invalid">
</div>
I am setting the variable submitted to true on save button click. It's working Okay the first time, but after the first submission, if enter the value for the input field(required field) and clear it, it's kicking off the validation(the top div shows).
Is there a way to show the validation div, only on the submit of the form and not when the user clears the input field ?
UPDATE
$scope.save = function (myForm) {
$scope.submitted = true;
if (myForm.$invalid) {
return;
}
$scope.submitted = false;
}
Thanks !
Hmm one way to do it would be to watch the FormController's property $dirty and $setPristine() method, and use a variable hasError to show the error or not.
See this plunker as an example
JAVASCRIPT
controller('AppController', ['$scope', function($scope) {
$scope.hasError = false;
$scope.$watch('theForm.$dirty', function() {
$scope.hasError = false;
$scope.theForm.$setPristine();
});
$scope.save = function() {
$scope.hasError = $scope.theForm.$invalid;
if($scope.hasError) {
// perform error routine
} else {
// perform save routine
}
};
}]);
HTML
<body ng-controller="AppController">
<div class="error" ng-show="hasError">This is an error</div>
<form name="theForm" ng-submit="save()" novalidate>
<input type="text" name="text1" ng-model="text1" required>
<input type="text" name="text2" ng-model="text2" required>
<button type="submit">Submit</button>
</form>
</body>
Make sure you are setting submitted to false upon display of your validation div else it'll show up after each additional validation call.
$scope.save = function (myForm) {
$scope.submitted = true;
if (myForm.$invalid) {
$scope.submitted = false;
return;
}
}
Am quite new to AngularJS. The issue is i have a form with two fields- name and profile pic as shown in the code below. I am using ng-upload (https://github.com/twilson63/ngUpload). I want the 'Save' button to work only if either field is dirty and the upload isn't happening currently so that multiple post requests are not triggered on the user clicking on the 'Save' button. But looks like, $dirty works fine with the 'name' field but not with the 'profile pic' field. Am i just missing something? How to go about it keeping it as simple as possible for a beginner of AngularJS. Any help would be appreciated.
//Code
<form id='picUpload' name='picUpload' ng-upload-before-submit="validate()" method='post' data-ng-upload-loading="submittingForm()" action={{getUrl()}} data-ng-upload='responseCallback(content)' enctype="multipart/form-data">
<input type="text" name="name" data-ng-model="user.name" maxlength="15" id="user_screen_name" required>
<input type="file" name="profilePic" data-ng-model="user.profilePic" accept="image/*">
<div class="form-actions">
<button type="submit" class="btn primary-btn" id="settings_save" data-ng-disabled="!(picUpload.name.$dirty|| picUpload.profilePic.$dirty) || formUploading">Save changes</button>
</div>
</form>
//In my JS code
$scope.submittingForm = function(){
$scope.formUploading = true;
}
Regards!
I made a directive ng-file-dirty
.directive('ngFileDirty', function(){
return {
require : '^form',
transclude : true,
link : function($scope, elm, attrs, formCtrl){
elm.on('change', function(){
formCtrl.$setDirty();
$scope.$apply();
});
}
}
})
I haven't used ng-upload before, but you can use onchange event of input element. onchange event is fired whenever user selects a file.
<input type="file" onchange="angular.element(this).scope().fileNameChanged(this)" />
Javascript :
var app = angular.module('MainApp', []);
app.controller('MainCtrl', function($scope)
{
$scope.inputContainsFile = false;
$scope.fileNameChanged = function(element)
{
if(element.files.length > 0)
$scope.inputContainsFile = true;
else
$scope.inputContainsFile = false;
}
});
So now you can check if inputContainsFile variable is true along with dirty check of name field