Dynamic form addition/subtraction with angularjs - javascript

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.

Related

Add same form element below by clicking add button. using Angular Js

I have a form1 and a add button. The thing I want is to add same form as form2 below form1 on clinking add button. As I am new to Angular Js it will be very helpful if someone can help me out.
HTML
<div class="row">
<header class="panel-heading">
<h5 class="panel-title" style="padding-left: 1.5rem;">
<b>APPLICANTS</b>
<input class="btn btn-primary" style="float:right " type="button" value="Add" />
</h5>
<hr>
</header>
<div class="panel-body">
<div class="container">
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label class="control-label">Name</label>
<div>
<input type="text" class="form-control" id="inputDefault" ng-model="txtFullName">
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label class="control-label">Nationality</label>
<div>
<input type="text" class="form-control" id="inputDefault" ng-model="txtNationality">
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label class="control-label">Pin Code</label>
<div>
<input type="text" class="form-control" id="inputDefault" ng-model="txtPincode">
</div>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label class="control-label">House No.</label>
<div>
<input type="text" class="form-control" id="inputDefault" ng-model="txtHouseNo">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
You need to create an array of applicants model objects, let's call it applicantsArray, where every object will represent a single applicant form.
You need to initialize this array with one model empty on page load.
Then you need to wrap your form with ng-for and iterate over every applicant model. So the n-th form will be binded with the n-th model in the applicantsArray.
Last step will be to push an empty applicant model to applicantsArray when 'Add' is clicked. Then a new model is added and the DOM will render additional form for the new model.

How to control the disability state of HTML inputs using Javascript

I have been trying to cycle between the disability states of two inputs on my website by using a button attached to some JavaScript. My current code has two buttons in use which both disable one input and enable the other, however this is not very practical. I wanted to know how I can use one button and one script to cycle between the disability states of my inputs, this is my current code:
<script>
function switch_monthlyexpense()
{
document.getElementById("monthlyexpenses").disabled=false;
document.getElementById("monthlypexpenses").disabled=true;
}
</script>
<script>
function switch_monthlypexpense()
{
document.getElementById("monthlyexpenses").disabled=true;
document.getElementById("monthlypexpenses").disabled=false;
}
</script>
<button class="btn btn-primary" onclick="switch_monthlyexpense()" id="monthlyexpensestoggle">Expenses</button>
<button class="btn btn-primary" onclick="switch_monthlypexpense()" id="monthlypexpensestoggle">P Expenses</button>
<form id="expenses_form" style="visibility:visible;">
<div class="form-group">
<div class="form-group col-md-1">
<label for="monthlyexpenses" id="monthlyexpenseslabel">Monthly Expenses</label>
<input type="number" class="form-control" id="monthlyexpenses" placeholder="0" disabled>
</div>
<span class="input-group-text" id="dsign5">$</span>
</form>
<form id="pexpenses_form" style="visibility:visible;">
<div class="form-group">
<div class="form-group col-md-1">
<label for="monthlypexpenses" id="monthlypexpenseslabel">Monthly P Expenses</label>
<input type="number" class="form-control" id="monthlypexpenses" placeholder="0">
</div>
<span class="input-group-text" id="psign4">%</span>
</form>
<script>
// use a variable to note which one is disabled
let selected = "monthlyexpenses"
function switchDisabled () {
// toggle the from "monthlyexpenses" to "monthlypexpenses" or vice versa
selected = selected === "monthlyexpenses" ? "monthlypexpenses" : "monthlyexpenses"
// check if the DOM disabled state is the same as the selected
document.getElementById("monthlyexpenses").disabled = selected === "monthlyexpenses";
document.getElementById("monthlypexpenses").disabled = selected === "monthlypexpenses";
}
</script>
<button class="btn btn-primary" onclick="switchDisabled()" id="switch-button">Switch</button>
<form id="expenses_form" style="visibility:visible;">
<div class="form-group">
<div class="form-group col-md-1">
<label for="monthlyexpenses" id="monthlyexpenseslabel">Monthly Expenses</label>
<input type="number" class="form-control" id="monthlyexpenses" placeholder="0" disabled>
</div>
<span class="input-group-text" id="dsign5">$</span>
</form>
<form id="pexpenses_form" style="visibility:visible;">
<div class="form-group">
<div class="form-group col-md-1">
<label for="monthlypexpenses" id="monthlypexpenseslabel">Monthly P Expenses</label>
<input type="number" class="form-control" id="monthlypexpenses" placeholder="0">
</div>
<span class="input-group-text" id="psign4">%</span>
</form>
disabled is an attribute so you can set it by:
document.getElementById("myId").setAttribute('disabled', '');
And to enable the element:
document.getElementById("myId").removeAttribute('disabled');
Your first function will look like:
function switch_monthlyexpense()
{
document.getElementById("monthlyexpenses").removeAttribute('disabled');
document.getElementById("monthlypexpenses").setAttribute('disabled', '');
}

How to use Angular UI Bootstrap DatePicker Popup with ng-repeat

<div class="task-manager_block" ng-controller="ToDoCtrl">
<div class="form_block clearfix">
<form name="addToDo">
<input class="add-input" placeholder="I need to..." type="text" name="inputToDo" ng-model="formTodoText" ng-model-instant ng-minlenght ng-minlength="5" ng-maxlength="40" ng-required="true"/>
<button class="add-btn" ng-click="addTodo()" ng-disabled="! addToDo.inputToDo.$valid "><h2>Add</h2></button>
</form>
</div>
<div class="tasks_block" ng-controller="DatepickerPopupCtrl">
<div class="task_block col-md-3" ng-repeat="todo in todos">
<div class="text_block">
<p class="input-group">
<input type="text" class="form-control" uib-datepicker-popup="{{format}}" ng-model="dt" is-open="popup1.opened" datepicker-options="dateOptions" ng-required="true" close-text="Close" alt-input-formats="altInputFormats" />
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="open1()"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
</p>
<p>{{todo.text}}</p>
</div>
</div>
</div>
</div>
How can I open a single popup that I clicked on because now all of them are instantly opened independently of what I clicked.
This happens due to all the datepickers inside ng-repeat reference the same variable to check open state.
See this.
is-open="popup1.opened"
You will have to manage this state in a separate object.
For example,
In controller declare a empty object
let datePickersStat = {};
then in your HTML, you can use anything as object key as long as it's unique.
is-open="datePickersStat[todo.id]"
Hope that helps.

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();
});

jQuery .on() issue, console.log() works but element interaction doesn't

I am using this code inside $(document).ready(function(){ ... }):
$('#flavours').on('click', '.toggle-quantity', function(e){
e.preventDefault();
var $quantity_percent = $(this).parent().find('.quantity_percent');
if($quantity_percent.is(':hidden')){
console.log("is hidden");
$quantity_percent.show();
}else{
console.log("is not hidden");
$quantity_percent.hide();
}
});
What's happening is the console.log() is working, but the $quantity_percent is not showing/hiding.
Additionally, if I add an alert('test') to the beginning of the function (just after e.preventDefault) this also doesn't work, but the console.log() continues to work (it correctly logs 'is not hidden').
EDIT: Relevant HTML markup:
<div id="flavours">
<div class="row flavour" id="flavour_1" style="display: block;">
<div class="form-group">
<div class="col-xs-12">
<fieldset>
<legend>Flavour 1</legend>
<div class="col-sm-4">
<label>Flavour Name</label>
<input type="text" value="" name="flavour_name[]" data-flavour-id="1" id="flavour_name_1" class="form-control autocomp" placeholder="Flavour name">
<input type="hidden" value="" name="flavour_id[]" id="flavour_id_1" class="form-control flavour_id">
<p class="flavour_info"></p>
</div>
<div class="col-sm-6">
<label>Flavour Quantity <span class="fa fa-filter"></span> <span class="hidden-sm">Switch to </span>drops per ml</label>
<div class="input-group" id="quantity_percent">
<input type="text" class="form-control" id="flavour_percentage_1" name="flavour_percentage[]" placeholder="Flavour Quantity">
<span class="input-group-addon">%</span>
</div>
<div class="input-group" id="quantity_drops" style="display: none;">
<input type="text" class="form-control" id="flavour_drops_1" name="flavour_drops[]" placeholder="Flavour Quantity">
<span class="input-group-addon">drops/ml</span>
</div>
<p class="flavour_recommended_percentage"></p>
</div>
<div class="col-sm-2">
<label> </label>
<button class="btn btn-danger btn-block remove-flavour"><span class="glyphicon glyphicon-remove"></span> Remove</button>
</div>
</fieldset>
</div>
</div>
</div>
$(this).parent() is a label element which doesn't have a child with .quantity_percent
Also quantity_percent is an id and not a class. Use the ID selector
So use
var $quantity_percent = $('#quantity_percent');
instead of
var $quantity_percent = $(this).parent().find('.quantity_percent');
Note: IDs must be unique in HTML
EDIT: as per OPs comments
I updated it to a class rather than ID (as it's on a dynamically growing list)
Usage
var $quantity_percent = $(this).closest('.col-sm-6').find('.quantity_percent');
The .parent() targets the <label> therefore it can't find .quantity_percent

Categories

Resources