angular dynamic required inputs doesn't affect form.invalid property - javascript

I have these form inputs that I generate with *ngFor after clicking a button.
in these inputs, I put the required attribute but it seems like it doesn't affect the form.invalid
I got other required inputs that aren't generated dynamically and when they're not filled with values the submit button is disabled as I want it to be, so I guess the issue is that the dynamic inputs doesn't exist until I click the button.
In addition I didn't manage to bind these inputs since they're dynamic, and I want to get they're values and send to a service when submitting the form.
any suggestions how to do such a thing?
EDIT
I've added to my model object in component.ts this this.model.players = Array(this.model.numOfTeams*this.model.numOfPlayers).fill(new Result())
and to my html i added ngModel 2way binding. now the validation is working fine, but all of the fields with same name (for example firstName) are changed together when I type in one of them.
here is my component.html:
<div class="container">
<form #newSearch="ngForm" class="form" ngNativeValidate>
<div class="form-group pl-3">
<fieldset name="generatePlayerCards">
<legend>generatePlayerCards</legend>
<p>Please choose <b>number of teams</b> and <b>number of players for each team</b>:</p>
<input class="form-control mt-3" type="number" name="numOfTeams" id="numOfTeams" min="1" max="5" placeholder="Number of teams" [(ngModel)]="model.numOfTeams" required>
<input class="form-control mt-3" type="number" name="numOfPlayers" id="numOfPlayers" min="1" max="20" placeholder="Number of players for each team" [(ngModel)]="model.numOfPlayers" required>
<button class="btn btn-primary mt-3" type="button" (click)="generatePlayerCards()">Done</button>
</fieldset>
</div>
<div class="card-group">
<div *ngFor="let j of ngArr; let i = index; trackBy:trackByIndex;" class="col-4 mt-3">
<div class="card">
<div class="card-header text-left"><h5>New Player Data</h5></div>
<div class="card-body">
<label for="firstName{{i}}"></label>
<input type="text" name="firstName{{i}}" class="form-control my-1 mr-sm-1" autocomplete="off" placeholder="First Name" required [(ngModel)]="model.players[i].firstName">
<label for="lastName{{i}}"></label>
<input type="text" name="lastName{{i}}" class="form-control my-1 mr-sm-1" autocomplete="off" placeholder="Last Name" required [(ngModel)]="model.players[i].lastName">
<label for="score{{i}}"></label>
<input type="text" name="score{{i}}" class="form-control my-1 mr-sm-1" autocomplete="off" placeholder="Score" required [(ngModel)]="model.players[i].score">
</div>
</div>
</div>
</div>
<div class="col-3 mt-4">
<button [disabled]="newSearch.form.invalid" class="btn btn-primary mr-2" type="submit" (click)="search()">Submit</button>
<button class="btn btn-secondary" type="reset" (click)="resetForm()">Clear Form</button>
</div>
</form>
</div>

In Angular in order to have dynamic fields and validation it is best practice to do it using a Form Array.
It might seem complicated at first, but Reactive forms in Angular are an incredible tool that will let you achieve great results with little to no effort in future.
There are plenty video tutorials, this one is the best imho.

Related

Validate elements (jquery repeater)

I'm working on an e-commerce project that uses jquery repeater in the wizard. I need to loop through the elements of the input to make my validations. Here is my input form code.
<button data-repeater-create type="button" class="btn btn-dark btn-sm icon-btn ml-2 mb-2 float-right">
<i class="mdi mdi-plus"></i>
</button>
<div data-repeater-list="group">
<div data-repeater-item class="d-md-flex mb-2">
<div class="form-group mr-1">
<input type="text" class="mt-0 form-control form-control-sm" id="sku" name="sku" placeholder="sku">
</div>
</div>
</div>
It comes in the browers elements like this,
<input type="text" class="mt-0 form-control form-control-sm" id="sku" name="group[0][sku]" placeholder="sku">
you can see, its like an array. So I need to get all these elements and validated all. For other field validation I have use
<script src="https://cdn.jsdelivr.net/jquery.validation/1.16.0/jquery.validate.min.js"></script>
can I use this for the those field?
Thanks in advance.

How to load default form values into vue object?

Here is my embedded code:
HTML:
<div id="user-panel">
<div class="text-center">
{{ fillItem }}
</div>
<form method="POST" action="http://site5/user_account/experiences/17" accept-charset="UTF-8" v-on:submit.prevent="updateItem(17)">
<input name="_method" type="hidden" value="PATCH">
<!-- Employer Field -->
<div class="form-group col-sm-6">
<label for="employer">Employer:</label>
<input class="form-control" v-model="fillItem.employer" name="employer" type="text" value="Some Emplyer" id="employer">
</div>
<!-- Designation Field -->
<div class="form-group col-sm-6">
<label for="designation">Designation:</label>
<input class="form-control" v-model="fillItem.designation" name="designation" type="text" value="some designation" id="designation">
</div>
<!-- Submit Field -->
<div class="form-group col-sm-12">
<input class="btn btn-primary" type="submit" value="Save">
</div>
</form>
</div>
Javascript:
<div id="user-panel">
<div class="text-center">
{{ fillItem }}
</div>
<form method="POST" action="http://site5/user_account/experiences/17" accept-charset="UTF-8" v-on:submit.prevent="updateItem(17)">
<input name="_method" type="hidden" value="PATCH">
<!-- Employer Field -->
<div class="form-group col-sm-6">
<label for="employer">Employer:</label>
<input class="form-control" v-model="fillItem.employer" name="employer" type="text" value="Some Emplyer" id="employer">
</div>
<!-- Designation Field -->
<div class="form-group col-sm-6">
<label for="designation">Designation:</label>
<input class="form-control" v-model="fillItem.designation" name="designation" type="text" value="some designation" id="designation">
</div>
<!-- Submit Field -->
<div class="form-group col-sm-12">
<input class="btn btn-primary" type="submit" value="Save">
</div>
</form>
</div>
Here is a jsfiddle for that.
What I want to achieve is to load the default form field values into a vue object.
The form fields do have some default values, but vue makes them blank. How can I load the default form values in vue? I can't embed the values through direct vue object, as values are going to be dynamic, and Javascript code will be placed in a special.js file.
There is a explanation for v-model in the Doc:
<input v-model='something'>
is just syntactic sugar for
<input :value='something' #input="value => { something = value }">
So after you assign value to input, Vue would reset the value of input by something from data of Vue instance like above. That means you couldn't pass default form field value into Vue object by value attribute of input tag.
For initialization, you can try to directly set Vue object when you init Vue instance, or modify Vue object in Callback function of dynamic access to default values.
Hope for you a little help.

AngularJS fire ng-change when changing a model

I have a form that looks like this:
<div class="col-xs-12 col-lg-4">
<form name="deliveryForm" ng-class="{ 'has-error': deliveryForm.$invalid && !deliveryForm.contact.$pristine }">
<div class="form-group">
<div class="btn-group">
<label class="btn btn-default" ng-model="controller.order.lines[0].forDelivery" btn-radio="true" ng-change="controller.setDeliveryDetails()">For delivery</label>
<label class="btn btn-default" ng-model="controller.order.lines[0].forDelivery" btn-radio="false" ng-change="controller.findCollectionPoints()">For collection</label>
</div>
</div>
<div class="form-group" ng-if="!controller.order.lines[0].forDelivery">
<label class="control-label">Contact</label>
<input class="form-control" type="text" name="contact" ng-model="controller.model.contact" ng-change="controller.setDeliveryDetails()" autocomplete="off" required />
<input type="hidden" name="collectionPoint" ng-model="controller.model.collectionPoint" ng-change="controller.setDeliveryDetails()" required />
</div>
<div class="form-group">
<label class="control-label">Instructions</label>
<input class="form-control" type="text" name="instructions" ng-model="controller.model.instructions" ng-change="controller.setDeliveryDetails()" autocomplete="off" />
</div>
<div class="form-group">
<button class="btn btn-default" type="button" ui-sref="saveOrder.lines">Back</button>
<a class="btn btn-primary pull-right" ng-if="deliveryForm.$valid" ui-sref="saveOrder.confirm">Continue</a>
</div>
</form>
</div>
As you can see, if my first line is not for delivery, then I show the contact input and the hidden collectionPoint input.
A bit further down I have a link that changes the collectionPoint:
What I was hoping would happen is that the hidden input would detect the change and fire the controller.setDeliveryDetails() method, but it doesn't seem to work.
Is there a way I can do this?
ng-change is triggered when changes are made on an input not on the model.
What you can do is either run controller.setDeliveryDetails() on ng-click :
Or set up a watch in your controller on controller.model.collectionPoint
$scope.$watch(angular.bind(this, function () {
return this.model.collectionPoint;
}), function (newVal) {
controller.setDeliveryDetails();
});

Show Added Values on same page before submitting in form with multiple entry

I would like to add functionality to my existing form.So far my form allows only entries on per submit bases, meaning is if the user wants to add another entry, he/she has to go back to the form and add and click to submit again.I would like to show all added values on same page, cause I am planning to add a Javascript print function that will print the add entries before saving to database.I need all values in fields to be printed
<form method="POST" name="myForm" action="saveto.php">
<label> Speed Rating:</label>
<select name="speed_rating" required class="form-control" id="speed_rating" ></select>
<div class="form-group col-sm-8">
<label> Description:</label>
<select name="description" required class="form-control" id="description" >
</select>
</div>
<div class="form-group col-sm-4">
<label> Load Index:</label>
<select name="load_index" required class="form-control" id="load_index" >
</select>
</div>
<div class="form-group col-sm-6">
<label> Vendor:</label>
<select name="vendor" required class="form-control" id="vendor" >
</select>
<div class="note"> Recommended Vendor : <span id="recomvend"> </span> </div>
</div>
<div class="form-group col-sm-6">
<label> Quantity:</label>
<input type="text" required name="qty" class="form-control" id="qty"></div>
<div style="clear:both"> </div>
<input type="submit" name="submit" class="btn btn-primary smp" value="Submit">
<button id="clear" type="button" class="btn btn-primary smp smp2" > Reset </button>
</div>
<input type="submit" value="submit" name="submit">
</form>
entry 1
speed rating -- 46
description ---'the discription'
------
entry 2
speed rating --56
description ---'another description'
submit
The saveto.php is just a normal script that will process the submitted data to database, one entry in a per submit bases.

Dynamic form addition/subtraction with angularjs

I am trying to get my dynamic form additions/subtractions to work correctly. The situation is that I am able to get the form block to add or remove, however, when I click the remove button it removes the most recently added block rather than the one I click on.
For example, if I add two new form blocks for a total of 3 blocks (block1, block2, block3) and I click remove on block2, instead of removing block 2 it removes block3.
I have created a plunker that demonstrates this, but it ONLY works when you launch the preview side in a separate window (otherwise the add button is inactive for some reason).
Working Example (must open in popup preview in plunker to function): plunker
<form class="form-horizontal" name="cpdForm" novalidate="" ng-submit="processForm()" ng-show="!message">
<h2>Subcontractor Performance</h2>
<hr />
<div ng-repeat="subcontractor in subcontractors">
<div class="well well-sm">Subcontractor #{{subcontractor.id}} <span id="subCounter"></span>
<button type="button" ng-click="removeSub()" class="close" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="form-group">
<div class="col-md-6">
<label for="subName">Subcontractor Name</label>
<input type="text" class="form-control" id="subName" name="subName" placeholder="Subcontractor" ng-model="formData.subName['subName'+($index+1)]" />
</div>
<div class="col-md-3">
<label for="mwbeCert">Disadvantaged Certification</label>
<select class="form-control" name="mwbeCert" ng-model="formData.mwbeCert" required="">
<option value="">Select MWBE Certification...</option>
<option ng-repeat="item in dropdownpoll['mwbecert']" value="{{item.mwbeid}}">{{item.mwbe}}</option>
</select>
</div>
<div class="col-md-3">
<label for="subAmount">Contracted Amount</label>
<div class="inner-addon left-addon">
<i class="glyphicon glyphicon-usd"></i>
<input type="text" class="form-control" id="subAmount" name="subAmount" placeholder="Contracted Amount" ng-model="formData.subAmount" />
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-4">
<label for="subContactName">Contact Name</label>
<input type="text" class="form-control" id="subContactName" name="subContactName" placeholder="Contact Name" ng-model="formData.subContactName" />
</div>
<div class="col-md-4">
<label for="subContactPhone">Contact Phone</label>
<input type="text" class="form-control" id="subContactPhone" name="subContactPhone" placeholder="Contact Phone" ng-model="formData.subContactPhone" />
</div>
<div class="col-md-4">
<label for="subContactEmail">Contact Email</label>
<input type="text" class="form-control" id="subContactEmail" name="subContactEmail" placeholder="Contact Email" ng-model="formData.subContactEmail" />
</div>
</div>
<div class="form-group">
<div class="col-md-3">
<label for="subRating">Subcontractor Rating</label>
<select class="form-control" name="subRating" ng-model="formData.subRating" required="">
<option value="">Select Subcontractor Rating...</option>
<option ng-repeat="item in dropdownpoll['subrating']" value="{{item.subratingid}}">{{item.rating}}</option>
</select>
</div>
<div class="col-md-9">
<label for="subComment">Comments</label>
<input type="text" class="form-control" id="subComment" name="subComment" placeholder="Comments" ng-model="formData.subComment" />
</div>
</div>
<hr />
<button type="button" class="btn btn-info btn-sm pull-right" ng-show="showAddSub(subcontractor)" ng-click="addNewSub()">[+] Add New Sub</button>
</div>
<input type="hidden" style="display:none;" ng-model="formData.subCount" value="{{subcontractor.id}}" />
<div class="form-group">
<div class="col-sm-12">
<button class="btn btn-primary" id="submit" ng-click="submitting()" ng-disabled="buttonDisabled">{{submit}}</button>
</div>
</div>
</form>
<pre>{{formData}}</pre>
you are splicing $scope.subcontractors-1 which only behaves like it does, ie, removes the last one.
try using $event.currentTarget as a general rule, it will indicate the clicked item exactly, and don't forget to pass $event as a parameter for the remove function.
I hope this helps you
You need to pass an index to the function removeSub
I modified your fn to accept an index
$scope.removeSub = function(ind) {
var newItemNo = $scope.subcontractors.length-1;
$scope.subcontractors.splice(ind-1, 1);
};
and in your HTML you just pass the currently iterated $index
<button type="button" ng-click="removeSub($index)" class="close" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
Hope this works. But be careful your formdata and subcontrators array are not in sync after this, thats a programming logic I guess you have to fix.
Happy coding
Cheers.
Joy
Not a full answer but your not telling the function in your script.js which one to remove your just telling it to chop one off the end. Try setting it up so that you pass the current subContractor id like ng-click="removeSub({{subcontractor.subContractorId}})" (not sure what your data model is) you could also reference the index but I believe that would be harder.

Categories

Resources