Validation directive code is executed on every item in ng-repeat - javascript

I am writing a single page application using Angular and Breeze. I have a set of entities displayed on my page using data-ng-repeat.
I am doing in place validation on the entities by having an "edit mode" div and a "display mode" div that are alternately hidden and shown using data-ng-hide and data-ng-show. One one entity at time can be in edit mode.
In the edit mode div, I have a bunch of inputs bound to the Breeze entity using data-ng-model and I am hooking in the Breeze entity validation using data-z-validate as described in the Breeze labs docs for their Angular validation directive:
http://www.breezejs.com/breeze-labs/breezedirectivesvalidationjs
The HTML looks like this (cut down for clarity):
<div data-ng-controller="payees as vm">
<div data-ng-repeat="payee in vm.payees">
<!--Display mode content-->
<div data-ng-hide="payee.editing">
<span>{{payee.name}}</span>
<span>{{payee.addressLine1}}</span>
<span>{{payee.town}}</span>
<span>{{payee.postcode}}</span>
<!--Some more markup here to switch to edit mode-->
</div>
<!--Edit mode content-->
<div data-ng-show="payee.editing">
<input data-z-validate data-ng-model="payee.name" />
<input data-z-validate data-ng-model="payee.addressLine1" />
<input data-z-validate data-ng-model="payee.town" />
<input data-z-validate data-ng-model="payee.postcode" />
<!--Some more markup here to save changes or cancel edit mode-->
</div>
</div>
</div>
The problem is that each time I type in one of the input, the validate directive code is executed for every input on every item in the repeater - even the ones that are hidden. I can see this by putting a breakpoint in my debugger. When I have lots of entities, surely this will make the responsiveness of the page very bad. In my code example, if I have 100 payees, then the validation code will execute 400 times (one for each of 4 properties on each of 100 payees), when really, it only needs to execute 4 times.
My question is: Is there a way of making the validation happen only for the entity that is currently in edit mode, or equivalently, only for the inputs that are not hidden?

For mutually exclusive conditions (for instance, a ngShow and a ngHide with the same value), you should use the ngSwitch directive, as recommended in the AngularJS FAQ:
Note especially the powerful ng-switch that should be used instead of several mutually exclusive ng-show.
In your case, with ngSwitch and in "display mode", the <input> tags will not exist anymore, and the validation directives will not be executed.
<div data-ng-controller="payees as vm">
<div data-ng-repeat="payee in vm.payees" data-ng-switch="payee.editing">
<!--Display mode content-->
<div data-ng-switch-when="false">
<span>{{payee.name}}</span>
<span>{{payee.addressLine1}}</span>
<span>{{payee.town}}</span>
<span>{{payee.postcode}}</span>
<!--Some more markup here to switch to edit mode-->
</div>
<!--Edit mode content-->
<div data-ng-switch-default>
<input data-z-validate data-ng-model="payee.name" />
<input data-z-validate data-ng-model="payee.addressLine1" />
<input data-z-validate data-ng-model="payee.town" />
<input data-z-validate data-ng-model="payee.postcode" />
<!--Some more markup here to save changes or cancel edit mode-->
</div>
</div>
</div>

Related

Accessible HTML Multistep Form

Are there any accessibility concerns with implementing multi-step forms as 1 big form (1 <form> tag) with multiple fields whose visibility that are handled via javascript logic as opposed to having multiple forms (multiple <form> tags) where the actual forms themselves are managed by javascript.
In short, would you rather:
METHOD 1
<form>
<div id="step-1">
<input />
<input />
</div>
<div id="step-2">
...
</div>
</form>
Another sub-question here if we do pick this method. Should the steps then be fieldset tags?
or
METHOD 2
<form id="form-1">
<div id="step-1">
<input />
<input />
</div>
</form>
<form id="form-2">
...
</form>
If we choose this way of doing it, is there anything that should be done to tell the user they are on the same form (through attributes or things of the sort)?
If it makes any difference, it may be worth noting that I am developing a single page application.
It's not very clear how you plan to move from one step to another. Anyway:
Fieldsets won't hurt, especially if you mark up real groups with them (such as radio groups or related checkboxes).
Make it clear that your user moves to a next step, like: add a Next Step (or Next>) button.
After your user presses the Next Step button, autofocus the first element of the next step, it greatly helps.
As for single form vs. multiple forms, it's up to you, but I would go for a single form, especially in an SPA.

Angular losing scope in ng-form template

I am trying this ,
<div ng-form="sadadPaymentForm" class="sadadPaymentForm" ng-if="vm.isSadadEnabled" name="sadadPaymentForm" validate-popup-form>
<div ng-if="vm.isSadadEnabled">
<div class="credit-debit-card-div" ng-include="'sadadTemplate'" ng-show="vm.view_tab == 'tab7'">
</div>
</div>
</div>
<span ng-show="vm.view_tab=='tab7' && vm.isSadadEnabled">
<button type="button" class="primary inline pay-now" id="paynowbtn" ng-disabled="!vm.checked3 || vm.sadadPaymentForm.$invalid" ng-click="vm.payByVoucher();" analytics-on="click" analytics-event="uaevent" analytics-eventcategory="Payment" analytics-eventaction="PayNow"
analytics-eventlabel="Pay now">
Pay by sadad
</button>
</span>
And , my template in another html file
<script type="text/ng-template" id="sadadTemplate">
<input fieldlabel="Online Payment ID" type="text" name="onlinePaymentId" ng-model="vm.sadadpayment.onlinePaymentId" class="form-control input-type-text"
ng-model-options="{ debounce: 100 }" validationKey-required="PaymentAdress1IsRequired" required maxlength="200">
</script>
Here the vm.sadadPaymentForm.$invalid does not work but Indidual components validation works on blur on blur of input .
BUT , if I add vm to ng-form ,ie like this
<div ng-form="vm.sadadPaymentForm" class="sadadPaymentForm" ng-if="vm.isSadadEnabled" name="vm.sadadPaymentForm" validate-popup-form>
<div ng-if="vm.isSadadEnabled">
<div class="credit-debit-card-div" ng-include="'sadadTemplate'" ng-show="vm.view_tab == 'tab7'">
</div>
</div>
</div>
Here the vm.sadadPaymentForm.$invalid works but Indidual components validation fails on blur of input for eg, TypeError: Cannot read property 'onlinePaymentId' of undefined
Help me understand how can I make both individual validation and the final form validation work together.Angular Ver 1.5,cannot upgrade this now.Need a solution with 1.5.
Form name attribute should have sadadPaymentForm inspite of vm.sadadPaymentForm. Since you don't have form name specified correctly, validation is failing.
name="vm.sadadPaymentForm"
should be
name="sadadPaymentForm"
Ok found out the issue , its basically a scope issue.
replaced
ng-if="vm.isSadadEnabled"
with
ng-show="vm.isSadadEnabled"
ng-if was preventing the DOM from rendering thus killing the scope variable vm itself

Several radio button groups in nested ng-repeat, but only last group shows the value

I have a page where I want to update a form with several radio buttons. I query an api, and use the returned array of objects to populate the current values for the radio buttons. The problem that I have is that only the last set of radio buttons actually shows the value. This is the code that I have (I am using [[ and ]] for the start and end symbols for angular):
<fieldset data-ng-repeat="s in sections">
<div class="form-group">
<div class="col-md-12">
<h2>[[ s.section.name ]]</h2>
</div>
</div>
<!-- Field Item -->
<div class="form-group m-b-20 bg-light" data-ng-repeat="f in s.fields">
<div class="col-md-12 m-b-30">
<h4>[[ f.field.name ]]</h2>
<input type="text" data-ng-model="f.comments" class="form-control input-md underline" placeholder="Comments">
</div>
<div class="col-sm-3">
<input type="radio" name="section-[[s.section.section_id]]-field-[[f.field.field_id]]" value="pass" class="form-control" data-ng-model="f.field_condition">
<label class="eval-pass"><i class="fa fa-check-circle green"></i> Pass</label>
</div>
<div class="col-sm-3">
<input type="radio" name="section-[[s.section.section_id]]-field-[[f.field.field_id]]" value="fail" class="form-control" data-ng-model="f.field_condition">
<label class="eval-fail"> <i class="fa fa-exclamation-circle red"></i> Fail</label>
</div>
<div class="col-sm-3">
<input type="radio" name="section-[[s.section.section_id]]-field-[[f.field.field_id]]" value="n/a" class="form-control" data-ng-model="f.field_condition">
<label class="eval-na"> <i class="fa fa-circle blue"></i> N/A</label>
</div>
<div class="col-sm-3">
<input type="radio" name="section-[[s.section.section_id]]-field-[[f.field.field_id]]" value="caution" class="form-control" data-ng-model="f.field_condition">
<label class="eval-caution"><i class="fa fa-exclamation-triangle yellow"></i> Caution</label>
</div>
</div>
[[ f.field_condition ]]
<hr>
</fieldset>
So basically, I have several sections, and each section has several fields. Each field has it's own radio button group (I am using the section and field ids to name the radio group). What I currently see is only the last field in each section actually shows the selected radio button. The other fields don't have any selection, even though the value for ng-model definitely does (I am showing the value of f.field_condition just to make sure there is a value).
For each field, I can see that the model is set. And if I select a value manually, I can see that the model changes, so it seems to me that the model is setup correctly. I just don't know why it won't initially show as selected for all rows but the last one.
I should also mention that if I save the form even with the missing radio button selections, the database is updated properly (it doesn't set the values to null, and if I manually change the selected value, it is updated in the db as well).
Does anyone have any ideas? Thanks!
EDIT
Here is a fiddle for this, although, it is working as expected in the fiddle. http://jsfiddle.net/dq8r196v/367/
I tried using the static data that I used in the fiddle, but I am still having the same problem. Does anyone know if this could be a CSS problem? The radio buttons are styled, and I didn't write the HTML or CSS.
UPDATE
I am still having this issue, so I built a new angular app and only used the code that is included in the fiddle that I have created. I am having the same problem with this new app, even though the same code works in the fiddle. I really don't understand what's happening here, but if anyone could shed some light, I would really appreciate it.
I have literally copied and pasted the code from my fiddle into a new angular app, and only the last group of radio buttons in each section is showing the value in the app.
Here is my complete code for the new angular app if someone else wants to try it out and see exactly what is happening: https://pastebin.com/qSR33yfM
I created the app on a single page for simplicity.
Here is the link to a pastebin with the exact json that I am using in my app: https://pastebin.com/utfVVQfT
I fixed the problem you're having by simply adding an array of objects ($scope.values) representing the different radio button options, and using an ng-repeat to create your radio buttons. See the following for the updated code: https://pastebin.com/s3hNzaXX
I know there are semantics around ng-repeat creating new $scopes, and imagine there is a conflict in scopes with your nested ng-repeats where it's binding to the radio buttons incorrectly and at a scope different than you want (the section level ng-repeat).
To confirm this suspicion, you could convert all of your interpolations in the code to use functions and console.log s and f at different points and confirm that field_condition is being set at a level you didn't intend.
Either way, it' best practice to create your radio buttons through data (and using ng-repeat), as is done with the $scope.values array, and a good side effect to doing this is not only can you update the different value options using data through AJAX or however you would like, but you won't have weird angular scoping issues as you're experiencing in your current code above.

creating dynamic forms using ng-repeat

Hello everyone I need to create some dynamic forms so users can configure feeds to their specification.
I have used ng-repeat to do the following:
For each feed a user needs to configure a new tab is created
for each property a feed has a label and input textbox is created.
Markup:
<tabset>
<tab ng-repeat="feed in feeds" heading="{{feed.heading}}">
<form role="form">
<div class="row" ng-repeat="property in feed.properties">
<div class="col-xs-6">
<div class="input-group">
<span class="input-group-addon">
<span>{{property.name}}</span>
</span>
<input type="text" class="form-control" value="{{property.value}}">
</div>
</div>
</div>
</form>
</tab></tabset>
This works just fine with the backing json that I have however I am wondering what the accepted way of capturing the data for this kind of use case, obviously I won't know how many feeds or properties each feed has so I suppose I need to bind this to an array in some way.
The question is how?
use ng-model
<input type="text" class="form-control" ng-model="property.value">
This way the textbox is bound to property.value. angular automatically updates property.value when you change the text in the textbox. You can use it in your JS just like any other variable. That's the beauty of angular.

First form of nested form tags not responding to css display:none

I have nested form tags like this
<form>
<h5>Main Form</h5>
<input type="text" />
<!-- Don't Show This Form -->
<form style="display:none">
<input type="text" />
<input type="text" />
</form>
<!-- Don't Show This Form -->
<form style="display:none">
<input type="text" />
<input type="text" />
</form>
</form>
The problem is the first form displaying although It's display none css inline
See the code in action http://jsfiddle.net/fZMKB/
I know I know, nested forms is against the rules But I have to use it this way for this reason
I need to reset bunch of inputs and form elements before jQuery event and I'm using this code
$('form').get(0).reset();
From this my earlier question How to reset forms elements (input,select,textarea,etc.) on events using jQuery
So the only reason I use form tag is I need to reset inputs and textarea, etc..
There's never a good reason to have nested forms. Instead, use proper HTML syntax and adjust your jQuery code accordingly. Here's some valid HTML markup:
HTML
<form>
<h5>Main Form</h5>
<input type="text">
<div class="one" style="display:none">
<input type="text">
<input type="text">
</div>
<div class="two" style="display:none">
<input type="text">
<input type="text">
</div>
</form>
Let's say you want to reset all text inputs, checkboxes, radio buttons, and select menus in the first subsection. This would only take two simple lines of jQuery:
$(".one input, .one select").val("");
$(".one textarea").html("");
If you want to restore default values, you should store the values in the HTML markup using the data attribute. Here's an example: http://jsfiddle.net/pgNrF/3/
You cannot have nested forms in HTML , you can have different forms but not nested
See this http://www.w3.org/TR/2011/WD-html5-20110525/forms.html#the-form-element
Content model
Flow content, but with no form element descendants.
No, nested forms are forbidden.
This is expressed in the HTML 4.01 DTDs as:
<!ELEMENT FORM - - (%block;|SCRIPT)+ -(FORM) -- interactive form -->
— http://www.w3.org/TR/html4/interact/forms.html#h-17.3
A FORM has a mandatory start tag, mandatory end tag and can contain anything in %block or SCRIPT, except other FORMs.
XML DTDs aren't as expressive as SGML DTDs so in XHTML this rule is specified only in the human readable text of the specification:
form must not contain other form elements.
— http://www.w3.org/TR/xhtml1/#prohibitions
HTML 5 isn't an SGML application and doesn't have an official machine readable description of the language. It also expresses this rule in text:
Content model:
Flow content, but with no form element descendants.
— http://www.w3.org/TR/html5/forms.html#the-form-element
Reference

Categories