AngularJS - validation not based on form element - angularjs

Is there a way to inject some validation - custom or otherwise - that isn't tied to a form element? Like - validate that some condition is met, but have it work with standard AngularJS validation?
Update:
Here's what I'm trying to accomplish:
I have a form contains a list of sections. Each section is controlled by a checkbox, and the section will display (via an ng-if) when the checkbox is checked.
Within each section, there's an opportunity for an item to be selected via a popup modal that is activated by a button click. Until an item is selected for that section, the form needs to be invalid. Once an item is selected for each selection that is checked, then the form needs to be valid.
I have a button at the bottom of the form with an ng-disabled="frm.$invalid". I want that to stay disabled until each section that has been checked contains an item that was selected via the modal.
Update 2:
Here's some example code:
<form class="form-horizontal" role="form" name="frm" novalidate>
<label>Name: </label>
<input type="text" ng-model="name" required/>
<div ng-repeat="orderItem in orderItems">
<input type="checkbox" name="items[]" ng-model="orderItem.selected"/>
<div ng-if="orderItem.selected">
... bunch of form fields
<button ng-click="openExchangeSelectionModal(orderItem)">Select Item</button>
<div ng-show="orderItem.exchange_item">
Selected Item: {{orderItem.exchange_item.name}} - ${{orderItem.exchange_item.price | number: 2}}
</div>
</div>
</div>
<button ng-disabled="frm.$invalid" ng-click="submitOrder">Submit</button>
</form>

Checking if the form is valid won't help you in this case since there is no required input that's not being filled.
What I would recommend is that the disable button would call a function that makes some logic about the number of check boxes expanded and the number of selected items and returns the buttons state (true for active otherwise false)
Let's take the example code you put up:
//some html tags...
<button ng-disabled="checkValid()" ng-click="submitOrder">Submit</button>
</form>
Notice that I changed the frm.$invalid to checkValid function, which is a function that is defined on your controller and can perform any logic you want to determine rather show your submit button or not.

Related

Angular Radio Buttons Form Validation

I'm generating a group of radio buttons options from a JSON object which is requested when another radio button is selected. This works as expected but the form validation is not working correctly since the form only becomes valid if all the options in the radio button group are first clicked.
My mark-up for the radio button:
<div data-ng-repeat="option in options" class="radio">
<label>
<input type="radio" name="decline_type_id" ng-model="decline_type_id" value="{{option.id}}" ng-required="!decline_type_id" />
<strong>{{option.name}}</strong>
</label>
</div>
Here is my plunker:
https://plnkr.co/edit/B7KgUt4GMrnSATYIQuN5?p=preview
The same mark-up without the loop works as expected, so I don't understand what it is about the loop used to generate the list that is breaking the validation of the form until all options are selected?
This is a Angular scoping issue. The ng-model inside the ng-repeat is using a child scope.
The ng-model="decline_type_id" was unique for each iteration of the loop. So ng-required check was for each unique model.
You can make use of $parent scope to do the ng-required checking on a shared variable instead.
<input type="radio" ng-model="$parent.decline_type_id" value="{{option.id}}" ng-required="!$parent.decline_type_id" />

How to reset a form in AngularJS

I'm working in AngularJS with a very simple form that only has a textarea input and a submit button. The textarea is a required field. Everything works... until I've submitted the form. When I remove the value from the model after submitting the form, this triggers my validation errors. If I don't set the value to null or an empty string, the textarea retains the entered value, which is not what I want.
<form name="notesForm" class="form-horizontal" ng-submit="vm.addNotesForm(notesForm)" novalidate>
<div control-validator="" validator-condition="vm.hasTriedToSubmit">
<div class="col-sm-10">
<!-- hidden field workaround for validation for textarea -->
<input type="hidden" id="hiddenNewNote" name="hiddenNewNote" required ng-model="vm.newNote.note" />
<textarea id="newNote"
name="newNote"
class="form-control"
placeholder="Note"
ng-disabled="vm.isWorking"
ng-model="vm.newNote.note"
rows="3"></textarea>
<control-validator-message>Note is required.</control-validator-message>
</div>
<div class="col-sm-2">
<button class="btn btn-success" type="submit" ng-disabled="vm.isWorking">
<i class="fa fa-save"></i>
Add Note
<i class="fa fa-circle-o-notch fa-spin" ng-show="vm.isWorking"></i>
</button>
</div>
</div>
Since validation only works for input fields, the hidden field is necessary.
this.notesService.addNote(this.newNote).then(() => {
this.notificationService.success('Successfully added new AR Note');
this.isWorking = false;
this.newNote.note = null; // <- This is where it goes sideways.
this.refreshDataTable();
}, errorMessage => {
this.notificationService.error(errorMessage);
this.isWorking = false;
});
I've tried all sorts of solutions. I used the FormController to set the form to pristine and untouched - no luck. I saw a proposed solution where the controls on the form were programmatically set to 'undefined'. Still no luck. Either the form submits and my value stays there in my textarea, or I set the note to null/empty string, and the validation errors get set off as if I've tried to post without a value in the textarea. As a total hack I even tried using jQuery to set the value of the textarea to an empty string, while keeping the model state undisturbed. While this did appear to work, since the value is still stored in the model, if a user clicks the add button again, it reposts. Not what I need. I'd think this would be a cinch, but after an hour of looking for an answer online, I've come up with nothing that works.

form submitted when dynamically adding fields with ng-repeat

I am dynamically creating form fields using ng-repeat. I have a div with several inputs/selects and when I click an "add" button, they gets duplicated. No problems so far. However, I also want to perform form validation when the submit button is clicked. Right now, I am doing the following to alert users a field is invalid when the submit button is pressed:
.ng-submitted select.ng-invalid, .ng-submitted input.ng-invalid {
background-color: red;
}
This works as expected provided I have not dynamically added inputs to the form. If I add new inputs to the form, .ng-submitted gets added to my form element (without submit being pressed) and all the required and invalid inputs turn red. You can see an example of this here: https://plnkr.co/edit/hDe40mBDjw9aDSDoAhe6?p=preview
Not only do I not want the inputs turning red when simply adding elements, I also don't want the form submitting unless someone has hit the submit button. There must be something I don't understand about submit with angular forms and help would be appreciated.
EDIT: further testing shows that the button element is the problem.
<button ng-click="addRow()">add</button>
This works fine however:
<input type="button" ng-click="addRow()" value="add"/>
Not sure why this is though.
The default value of the type of a button is "submit". So clicking it submits the form. Putting type="button" explicitly prevents the form being submitted when the button is clicked.
If you want to use form submit you need to use ng-submit on form and change button type to submit.
<form name="form" ng-submit="addRow()">
<div>
<div ng-repeat="row in rows track by row.id">
<input type="number" min="0" ng-model="row.id" required/>
</div>
<button type="submit">add</button>
</div>
</form>
There were multiple changes needed in your code. I have fixed them in this plunker: https://plnkr.co/edit/GEWdCo1cQPPEFJcZxT58?p=preview

ng-click function evaluating on second click (or, unclick)

--- HTML ---
<div class="btn-group menuSection">
<div class="menuGrid" ng-repeat="main in mains">
<button type="button" class="btn btn-default btn-sm btn-block"
ng-value="main" btn-radio="main" ng-model="$parent.selectedMain"
ng-click="setDish();"/>
{{main}}
</div>
<div class="menuGrid">
<input type="text" class="form-control input-sm"
ng-model="mainOtherText" ng-show="selectedMain == 'Other'"
placeholder="enter other here"/>
</div>
test : {{mainDish}}
</div>
--- JS ---
$scope.setDish = function(){
if ($scope.selectedMain == 'Other') {
$scope.mainDish = $scope.mainOtherText;
}
else {
$scope.mainDish = $scope.selectedMain;
}
};
I have a grid of radio buttons for selecting menu items, the last of which is 'Other'. When 'Other' is selected, a textbox shows up where the user inputs another item. My goal with the ng-click function is to update the variable mainDish with the value in the other textbox when 'Other' is selected.
This code works, however, the problem arises in that the variable mainDish only updates when the radio button is unselected.
Example - when a button is first clicked, it will have no affect on the variable mainDish, but then when another button is clicked mainDish is updated to the value of the first button clicked.
Any ideas on to why this is happening or ways I could fix it?
EDIT:
As per comment, I'll explain my code a bit more. I have a menu, a list of meals that the user picks from. This list is populated by the ng-repeat command grabbing from an array of meals and creating a radio button for each. The last item in the array is an item called 'Other'. When 'Other' is selected, a textbox appears so the user can input their own dish.
What I want: I want the variable mainDish to be assigned the value of the 'Other' textbox when the 'Other' radio box is selected. For all of the other items, the value of mainDish is just the value of the radio button, but this is not the case with 'Other'.
What I have: It works! But in an odd way. The variable mainDish is only updated when the radio button is deselected. Every time you click a new radio button, the variable mainDish gets assigned the value of the previous button. This is the problem I need help solving.
The problem is that ng-click fires before the ng-model code that updates the scope. If you change it to use ng-change, that will fix it. You also need to add ng-change to your text box to update the scope as the user types.
<div class="btn-group menuSection">
<div class="menuGrid" ng-repeat="main in mains">
<input type="radio" name="dishes"
class="btn btn-default btn-sm btn-block"
ng-value="main"
ng-model="$parent.selectedMain"
ng-change="setDish();"/>
{{main}}
</div>
<div class="menuGrid">
<input type="text" class="form-control input-sm"
ng-model="mainOtherText"
ng-show="selectedMain == 'Other'"
ng-change="setDish();"
placeholder="enter other here"/>
</div>
test : {{mainDish}}
</div>
Here is a working example: http://jsfiddle.net/bnzwx94b/2/

AngularJS post form to external URL

I have the following form in my AngularJS app which contain hidden fields with values filled based on user selection on some inputs on the form (radio buttons...etc), when the user click on the Submit link I should route the user to an external URL while passing hidden fields just as any normal form submission. Unfortunately I can't do this as some of the hidden field values are dependent on some calculations inside a function of the view related controller (as shown below in controller code, so I was wondering is there a way I can call the controller function from this form, then the controller function post the whole form and its field? Any example is highly appreciated. Thanks.
Note I am using link instead of a button.
<form name="clientPaymentForm" id="clientPaymentForm" action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">>
<div>
<fieldset>
<input id="name" type="text" required placeholder="Client Name" ng-model="client.name">
...
...
<input type="hidden" name="amount" ng-value="order.total">
...
...
<a class="orderButton" href="javascript:{}" onclick="document.getElementById('clientPaymentForm').submit(); return false;">Order Now</a>
</fieldset>
</div>
</form>
Controller:
$scope.processOrder = function(){
//Order calculation happens here to update order.total value and can only happen after click click Order Now to place the order...
};
I guess this is a bit late, but what you want to use is the ng-click directive which will allow you to call functions defined directly on the scope.
Assuming that you've defined $scope.processOrder, change your a tag to the following:
<a class="orderButton" ng-click="processOrder()">Order Now</a>
And everything should work as hoped.
Alternatively, you could use ng-submit on the form to have it work when you press the "Enter" or "Return" key, as in:
<form name="clientPaymentForm" id="clientPaymentForm" action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top" ng-click="processOrder()">.

Resources