I am trying to find a simple solution to a required input type of scenario. I have multiple small forms that all send on one button save on the bottom of the page. What I am trying to accomplish is something like ngRequired, however across the whole controller, not just the individual forms. So the desired effect is pretty simple - if any of the inputs aren't filled out - set a boolean( or something) to false that disables the save button at the bottom.
So my first attempt is like this -
I have a model on each of the required items - there are 10 items
then I have a function that checks when you try to click the button how many are chcked like this
if($scope.modeltracker1){
//if there is anything inside model 1 add 1 to the tracker
$scope.modeltracker += 1;
}
and if the counter is not 10, don't do anything (all required are not filled out)
if($scope.modeltracker != 10){
//do nothing because all required are not filed out
}else{
//run save, all required all filed out
}
So - I feel like there should be a much easier solution than my first attempt here. Maybe something along the lines of checking if any individual one of these required fields is false, don't fire? I know that ngRequied would be great for this, but unfortunately the way this has to be structured, it cannot be one large form. There has to be a much easier way to accomplish this task with angular.
Any input would be much appreciated, thanks for reading!!
You can use ng-form to nest your multiple forms. It allows using nested forms and validating multiple forms as one form.
So, you need to nest your multiple forms in one root form.
<div ng-controller="demoController">
<form name="parentForm">
<ng-form name="firstForm">
<input type="text" ng-model="firstModel" required>
</ng-form>
<ng-form name="secondForm">
<input type="text" ng-model="secondModel" required>
</ng-form>
</form>
</div>
Then, all you need to do is to check parent form's validation status.
angular.module('formDemo', [])
.controller('demoController', ['$scope', function($scope) {
if($scope.parentForm.$valid) {
//run save, all required all filed out
} else {
//do nothing because all required are not filed out
}
}]);
you can use myForm.$invalid directive, as explained here: Disable submit button when form invalid with AngularJS
Related
I've two text boxes with different ng-models. They fill up using a $http.get request on click of a button.
Lets say:
<input type="text" ng-model="name.title" />
<input type="text" ng-model="name.surname" />
These get filled up just fine from my JSON data.
The value of my name.title can sometimes be like "abc (123)". If a user edits this, I want the name.surname to become the part inside the (). If the user removes "abc (123)" and just types in 123, then 123 should reflect in name.surname.
I've tried various combinations using ng-blur and ng-change but nothing so far has worked for me.
Its just one two textboxes so no point writing a new directive.
How do I do this?
Any help will be appreciated. :)
I asume that your using angular 1.x.
You could have a watch over name.
something like in you
$scope.$watch('[name.title, name.surname]', function(newValue, prevValue) {
if (newValue === prevValue) {
return;
}
//Here you put your logic and update the models.
});
What is the "Angular" recommended way to validate conditions that involve multiple fields on a form? Most or all of the validation examples that I have seen talk about custom validation directives that attach to a single textbox,select,etc. What about conditions that involve multiple fields on a form?
For example, I want to have an "empty" form validator: This will prevent submits on forms that have no required fields and all fields are empty.
Another example is, say I have a master/child one-to-many relationship on a page and the child relationship is a table of child records. What if I need to validate that at least one child record should exist IF 2 or 3 fields meet certain conditions?
One thought that I have is to built validation directives that attach to <form> tags as elements. Something like <form name="xxx" validate-not-empty > This directive will then set the $invalid property of the form, on submit. But I am concerned that this maybe is not the angular away to go as I have not seen this on any code samples I have seen. So I am looking for any alternatives to achieve this.
What is wrong with having your "not-empty" directive placed on each of your input field? (following this way of doing: http://angularjs.io/how-to-create-a-custom-input-validator-with-angularjs/)
Then you can just check anywhere if the form is valid, for example to disable the send button:
<button ng-disabled="!form.$valid">Send</button>
For it to work you must have all input with its ng-model.
I didn't quiet understand your master/child problem. It you could give an example...
I've used the basic validation, based on input type (email, tel, etc.) and the "required" attribute. But, more complex validation is typically handled through custom directives. Although, there may very well be a library that I'm not aware of that provides a common set.
As a basic example:
<form name="contactForm">
<input type="text" name="ContactName" ng-model="contact.name" required>
<button type="button" ng-click="submitForm(contactForm)" />
</form>
Then, in your controller:
$scope.submitForm = function (form) {
if (form.$valid) {
...
}
}
This W3Schools page describes Angular's validation fairly well.
AngularJS Form Validation
Toward the bottom of the article, they mention that custom validation requires a bit more effort. But, they provide examples.
To create your own validation function is a bit more tricky. You have
to add a new directive to your application, and deal with the
validation inside a function with certain specified arguments.
<form name="myForm">
<input name="myInput" ng-model="myInput" required my-directive>
</form>
<script>
var app = angular.module('myApp', []);
app.directive('myDirective', function() {
return {
require: 'ngModel',
link: function(scope, element, attr, mCtrl) {
function myValidation(value) {
if (value.indexOf("e") > -1) {
mCtrl.$setValidity('charE', true);
} else {
mCtrl.$setValidity('charE', false);
}
return value;
}
mCtrl.$parsers.push(myValidation);
}
};
});
</script>
Hope this helps.
What I am trying to do is take 4-5 inputs in a form, store to an object and when user selects 'save', temporarily store in session and be able to access to show that object in a row in a table on same page and then allow user to enter data form input again to add to next object.
So in the end, the user may enter 1 object or up to 6 and when done, allow user to submit all objects as an array of objects or something to that effect.
So I am not an angular power user, but figure it has this power somewhere. So my questions evolve around a good approach to this and maybe trying to find a good example or so. Any of you able to help on this?
Thanks much.
Well, this is actually quite easy if you have the table and the form on the same page. You can simply use one scope variable for the array of entered objects and one scope variable for the current object itself.
I'll now do an example without all of your requirements (e.g. that the user can enter up to six objects the most), but this should be quite straight forward to implement.
First take the HTML:
<form ng-controller="RolesController">
<label for="name">Name</label>
<input type="text" id="name" ng-model="current.name">
<label for="slug">Slug</label>
<input type="text" id="slug" ng-model="current.slug">
<label for="level">Level</label>
<input type="number" id="level" ng-model="current.level">
<!-- buttons -->
<button type="button" ng-click="storeTemp()">Store temporarily</button>
<button type="button" ng-click="storeAll()">Store all</button>
</form>
And the Controller:
angular.module('administration').controller('RolesController',
['$scope', '$http', function($scope, $http) {
$scope.current = {};
$scope.all = [];
$scope.storeTemp = function() {
/* do validation here or in html before ... */
$scope.all.push($scope.current);
$scope.current = {};
};
$scope.storeAll = function() {
/* maybe another validation ... */
$http.post('admin/roles', { roles: $scope.all });
};
});
Obviously you could also use the same button for both actions if you require your user to always input the same amount of new objects.
Ah, master-detail form!
You do not need to worry about the multiple inputs the user are savings. Just declare an array and push your objects into it every time the user click 'save'. As long as you do not destroy your controller (i.e no page reload, no switch of controllers, etc) your data are always there.
//master
$scope.toBeSentToServer = [];
//details, declare your object wherever according to your needs
$scope.formVariable = {
var1:"",
var2:"",
var3:""
};
$scope.clickSave = function(){
$scope.toBeSentToServer.push($scope.formVariable);
//reinstantiate your $scope.formVariable so that your form is empty
$scope.formVariable = {};
}
And now you can use our beloved ng-repeat to iterate through scope.toBeSentToServer inside the table.
<table>
<tr ng-repeat= "item in toBeSentToServer">
<td>{{item.var1}}</td>
<td>{{item.var2}}</td>
<td>{{item.var3}}</td>
</tr>
</table>
Of course, there will be times you would want to use the cookies / local-data-storage, there is a plethora of libraries available to do it. Angular has its own $cookies service but I would highly recommend angular-local-storage from Gregory Pike. (Sorry maybe bias)
But before all these, if you are looking for data persistent mechanism, maybe try using a factory or sevice in angularjs?
Or put in tl;dr code:
form.email.$setValidity('conflict', false);
is too sticky for my simple serverside validation flow.
I'm trying to get the form to show good feedback in the event that the user enters an email address already in use by another customer. I'm running AngularJS v1.2 and have this template:
<form name="form">
<input name="email" type="email" ng-model="..." required>
</form>
<div ng-messages="form.email.$error">
<div ng-message="conflict">Email address already in use.</div>
</div>
In my controller, I'll handle the submit event and trigger the validation in my $http.post().error handler like this:
$http.post('api/form/submit/path/here').error(function(resp) {
if (resp.details === 'conflict')
$scope.form.email.$setValidity('conflict', false);
});
The problem is that when the user goes back and changes the value in the input field, the error message doesn't go away. It sticks around until I manually call $scope.form.setValidity();.
The docs say implement a custom directive with an ng-model dependency, but that seems super overkill for my purposes. I've also tried setting $scope.form.email.$valid = false; and $scope.form.email.$invalid = true; but those don't change the appearance of the textbox.
Nothing in your code modifies the conflict validation key, except for when $setValidity('conflict', false) is explicitly called. Since that is the only code setting the state of the conflict validation key and there is nothing else resetting it to true, it's expected behaviour that editing the textbox wouldn't reset its conflict validation state.
To get the behaviour that you want, you need to code for it. One way is to use ng-change.
<input name="email" type="email" ng-model="..." required ng-change="resetConflictState()">
$scope.resetConflictState = function() {
$scope.form.email.$setValidity('conflict', true);
}
I have just started using Knockout.js My form has elements which I call questions. I hide/show them based on user selections. When user hits the submit button I want to post only the visible questions at the time of submit. What I have is this:
// length of Results(questionArray) is 260
var vmToPost = viewModel;
delete vmToPost.__ko_mapping__;
ko.utils.arrayForEach(vmToPost.Results(), function (question) {
if (!(vmToPost.getQuestion(question.QuestionID()).visible())) {
ko.utils.arrayRemoveItem(vmToPost.Results(), question);
}
});
The util function arrayForEach is behaving strange. It loops through the array very differntly. I had to hit the submit button 7 times to get all the visible elements and come out of the util function. It doesnt throw any error message in the console or the fiddler.
What am I doing wrong. Please help.
Html contains a built-in way to skip items from being submitted. It's the disabled attribute, which can be controlled using Knockout with the enable or disable bindings.
<div data-bind="visible: visible">
<label>Name: <input name="name" data-bind="enable: visible"></label>
</div>