I have a form in Angular setup like the following.
Users can make changes and submit one at a time and it works as expected but I'd like to add a "Submit All" that would submit each item one at a time as though the user were pressing the submit button for each. Im a little stumped on the best way to do this. As of right now I cannot submit them as a batch due to API constraints. I would also like to keep jQuery out of the equation.
My first thought is to create a new object that contains the info for each item and then loop over it and submit that way. I am unsure how to set this up in my controller.
<div class="table-responsive">
<table class="table table-striped">
<thead>
<th>Name</th>
<th></th>
<th>Age</th>
<th>Kids</th>
<th></th>
</thead>
<tbody>
<tr class="pending-item animate-repeat"
data-ng-repeat="user in Users"
data-ng-form="userForm"
role="form"
data-ng-submit="submitUser(user, userDetails)"
novalidate>
<td class="img-container">
<img data-ng-src="{{user['image']['url']}}"
alt="{{user['image']['alt'] || ' '}}"
class="img-responsive" >
</td>
<td class="col-xs-6">
<div class="user-info">
<p class="user-name">
{{user['name']}}
</p>
</div>
</td>
<td>
<div class="input-group input-group-sm">
<span class="input-group-addon">Age</span>
<input type="number" min="0"
name="age"
class="form-control age"
data-ng-init="userDetails.age = user['age']"
data-ng-model="userDetails.age"
required>
</div>
</td>
<td>
<div class="input-group input-group-sm">
<input type="number" min="0" step="1"
name="kids"
class="form-control kids"
data-ng-disabled="user['kids'] === true"
data-ng-pattern="/^\d+$/"
data-ng-init="userDetails.kidsCount = user['kids']['quantity']"
data-ng-model="userDetails.kidsCount"
required>
<div class="input-group-addon"># of kids</div>
</div>
</td>
<td>
<div class="btn-group btn-col">
<button type="submit"
class="btn btn-success btn-sm"
data-ng-disabled="userForm.$invalid || userDetails.working"
data-ng-click="submitUser(user, userDetails)">
Submit
</span>
</button>
</div>
</td>
</tr>
</tbody>
</table>
<button ng-click="submitAllUsers()">Submit All Users</button>
</div>
Yes, create a object in your controller, say $scope.formData={}, and then bind it with your elements using ng-model or data-ng-model and then on button click pass this object in your controller and do the required stuff.
Related
This question already has answers here:
Event binding on dynamically created elements?
(23 answers)
How can I apply a jQuery function to all elements with the same ID?
(4 answers)
Closed 1 year ago.
I have an input form that when the add button is clicked, it submits the current line of data and then adds a new row of input fields for more data entry. When I click the add button, it will post the first input data into the database and create a new row. However when I try to submit the second row, nothing happens. The data isn't submitted and another row isnt added.
This is my jquery click function
$('#addRow').click(function(e) {
const cloneRow = $('#tableData tbody tr').first();
e.preventDefault();
let data = {
project_id: $(".project_id").last().val(),
imp_or_ann: $(".imp_or_ann").last().val(),
category: $(".category").last().val(),
cost: $(".cost").last().val(),
hours: $(".hours").last().val()
}
$.ajax({
url: '/costs_hours',
type: 'POST',
data: data
}).then(
cloneRow.clone().appendTo('#tableData tbody').find(".cost, .hours").val(''),
$("#next").removeAttr('disabled'),
$("#link").attr('href', '/fundings')
)
})
This is my input form
<table id="tableData" class="table text-light text-center mt-3">
<thead>
<tr>
<th scope="col">Project ID</th>
<th scope="col">Implementation or Annual</th>
<th scope="col">Category</th>
<th scope="col">Costs</th>
<th scope="col">Hours</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div class="input-group mb-3">
<input name="project_id" type="text" class="form-control project_id">
</div>
</td>
<td>
<div class="input-group mb-3">
<div class="input-group mb-3">
<select name="imp_or_ann" class="form-select imp_or_ann"
id="inputGroupSelect01">
<option disabled selected>Choose...</option>
<option>Implementation</option>
<option>Annual</option>
</select>
</div>
</div>
</td>
<td>
<div class="input-group mb-3">
<div class="input-group mb-3">
<select name="category" class="form-select category" id="inputGroupSelect01">
<option disabled selected>Choose...</option>
<option>EMO</option>
<option>Analysts</option>
<option>Maintenance</option>
<option>ETS</option>
<option>BOT</option>
<option>OtherUT</option>
<option>Materials</option>
<option>Non-UT Contract</option>
<option>Contingency</option>
</select>
</div>
</div>
</td>
<td>
<div class="input-group mb-3">
<input name="cost" type="text" class="cost form-control">
</div>
</td>
<td>
<div class="input-group mb-3">
<input name="hours" type="text" class="hours form-control">
</div>
</td>
<td>
<button id='addRow' type="button" style="background-color: #bf5700;"
class="btn btn-warning text-light"><i
class="fas fa-plus-circle"></i> Add</button>
</td>
</tr>
</tbody>
</table>
I want the button to stay in the so the use knows they have to click the add button to add that piece of data. But this currently only works for the first data input. When the second row is added, The button doesnt work, I cant submit data and another row isnt created. Any advice is greatly appreciated!
$('#addRow') is evaluated once, when the JS code is executed.
When you clone and add new row, you are effectively created a new (and different!) element with id="addRow". THAT element does not have a handler listening for click events.
Instead, you need to put the listener on something that does not change, for example #tableData, using the jquery $.on():
$('#tableData').on('click', '.addRow', function(e){
...
});
This way, the element listening is #tableData, which gets clicks and then checks to see if the click originated on something matching .addRow. Your actual function doesn't need to change.
However, you will need to change your add button from id="addRow" to class="addRow", (or use name attribute or anything else) so you don't have multiple elements with same ID in the DOM.
Well, you were setting the same ID for multiple objects which is problematic, but what you really needed was to set the listener on a static parent (I used tableData). I changed your button ID to a class, so it could apply to all add buttons.
Your listener was set to scan the page for button#addRow and attach a listener. Then you create a new instance of the button, but the listener setup had already fired and wasn't aware of the newly created instance. Instead, set the listener on a static parent. The event is on with arguments of click to detect the click and button.addRow to specify which element to focus the listener on.
$('#tableData').on('click', 'button.addRow', function(e) {
const cloneRow = $('#tableData tbody tr').first();
e.preventDefault();
let data = {
project_id: $(".project_id").last().val(),
imp_or_ann: $(".imp_or_ann").last().val(),
category: $(".category").last().val(),
cost: $(".cost").last().val(),
hours: $(".hours").last().val()
}
$.ajax({
url: '/costs_hours',
type: 'POST',
data: data
}).then(
cloneRow.clone().appendTo('#tableData tbody').find(".cost, .hours").val(''),
$("#next").removeAttr('disabled'),
$("#link").attr('href', '/fundings')
)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id="tableData" class="table text-light text-center mt-3">
<thead>
<tr>
<th scope="col">Project ID</th>
<th scope="col">Implementation or Annual</th>
<th scope="col">Category</th>
<th scope="col">Costs</th>
<th scope="col">Hours</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div class="input-group mb-3">
<input name="project_id" type="text" class="form-control project_id">
</div>
</td>
<td>
<div class="input-group mb-3">
<div class="input-group mb-3">
<select name="imp_or_ann" class="form-select imp_or_ann" id="inputGroupSelect01">
<option disabled selected>Choose...</option>
<option>Implementation</option>
<option>Annual</option>
</select>
</div>
</div>
</td>
<td>
<div class="input-group mb-3">
<div class="input-group mb-3">
<select name="category" class="form-select category" id="inputGroupSelect01">
<option disabled selected>Choose...</option>
<option>EMO</option>
<option>Analysts</option>
<option>Maintenance</option>
<option>ETS</option>
<option>BOT</option>
<option>OtherUT</option>
<option>Materials</option>
<option>Non-UT Contract</option>
<option>Contingency</option>
</select>
</div>
</div>
</td>
<td>
<div class="input-group mb-3">
<input name="cost" type="text" class="cost form-control">
</div>
</td>
<td>
<div class="input-group mb-3">
<input name="hours" type="text" class="hours form-control">
</div>
</td>
<td>
<button type="button" style="background-color: #bf5700;" class="addRow btn btn-warning text-light"><i
class="fas fa-plus-circle"></i> Add</button>
</td>
</tr>
</tbody>
</table>
I want to build a pop-up modal edit form to change individual table values in a dynamic grid. Right now, the input fields appear on button click using the function editToggle(i). No more than 4 input fields will ever appear because they are meant to edit the 4 values in my data model. However, the input fields (and values) are being dynamically generated with *ngFor. I need some way to pass/copy those input fields to my modal to edit there instead of on the grid itself (where they currently appear after clicking the edit button).
I have tried to use [(ngModel)] to clone but it does not work. I have tried to pass them using functions but the values return null. Because the HTML only shows one input field (because they are being dynamically created with *ngFor) I do not know of a way to individually pass the values.
<div>
<table align="center">
<tr>
<th>
List of Providers
</th>
</tr>
</table>
<table id="thetable" align="center">
<tr>
<th>Application ID</th>
<th>Client Name</th>
<th>Version</th>
<th>API Key</th>
<th>Protected Secret</th>
<th>EDIT/DELETE</th>
</tr>
<tr ng-app="tblRowApp" *ngFor="let prov of providers; let i = index">
<td *ngFor="let col of columns">
<span class="field" *ngIf="i !== index">
{{prov[col]}}
</span>
<span *ngIf="i === index">
<input [(ngModel)]="inputClientName" class="table" value="{{prov[col]}}" (change)="EditItem(i, col, $event.target.value)" type="text" placeholder="{{prov[col]}}">
</span>
<td>
<span *ngIf="editing && i === index">
<button (click)="save()">Save</button>
</span>
<span *ngIf="i !== index">
<button class="edit" name="editButton" (click)="editToggle(i); openEditForm()">/</button>
<button class="delete" (click)="deleteRow(i)">x</button>
</span>
</td>
</tr>
</table>
<!-- The EDITING Modal -->
<div id="editForm" class="modal_edit">
<div class="modal-content_edit">
<span (click)="save()" class="close">×</span>
<h2 style="margin-bottom: 70px">Edit OAuthAppProvider</h2>
<div>
<label style="margin-bottom: 20px">
Client Name:
</label>
<input [(ngModel)]="inputClientName" id="editClientName" type="text">
</div>
<div>
<label style="margin-bottom: 20px">
Version
</label>
<input id="editClientVersion" type="text">
</div>
<div>
<label style="margin-bottom: 20px">
API Key:
</label>
<input id="editClientAPIKey" type="text">
</div>
<div>
<label style="margin-bottom: 20px">
Protected Secret
</label>
<input id="editClientProtectedSecret" type="text">
</div>
<button style="float: right" class="add" (click)="save()">
<h4 style="font-style: bold">Save</h4>
</button>
<button class="cancel" (click)="save()">
<h4 style="font-style: bold">Cancel</h4>
</button>
</div>
</div>
</div>
export const PROVIDERS: any[] =
[
{
AppID: "11",
ClientName: "sampleclientname1",
apiKey: "sampleapikey1",
Version: "1.0",
protectedsecret: "samplesecret1"
},
{
AppID: "12",
ClientName: "sampleclientname2",
apiKey: "sampleapikey2",
Version: "1.0",
protectedsecret: "samplesecret2"
},
{
AppID: "13",
ClientName: "sampleclientname3",
apiKey: "sampleapikey3",
Version: "1.0",
protectedsecret: "samplesecret3"
},
{
AppID: "14",
ClientName: "sampleclientname4",
apiKey: "sampleapikey4",
Version: "1.0",
protectedsecret: "samplesecret4"
}
]
You can set a variable named something like selectedRowData and set the provider as its value when the user clicks the edit button. The value attribute of the inputs on the modal can be set to the properties of the selected row. It's difficult to tell what the functionality of the other methods is supposed to be without the component code so I made some assumptions. Let me know if you have any other questions about it.
Here's a link to a StackBlitz.
EDIT
The data is only being bound one way via the [value] attribute and there isn't a form object keeping track of all the changes like there would be using Reactive Forms so a model should be created first.
I commented out the original solution and added updates below. The selectedRowData variable is instantiated with a provider object with empty properties. The modal has been updated to use two-way binding with [(ngModel)]. The StackBlitz has also been updated.
The table is updated as the user types their edits into the form. The save button doesn't need to be used unless the data needs to be persisted somewhere.
Check out the Angular Forms Documentation it should help with how to pass form data around between components. What you've created here is similar to Template-driven Forms.
Component
// selectedRowData = null;
selectedRowData = {
AppID: "",
ClientName: "",
apiKey: "",
Version: "",
protectedsecret: ""
};
editToggle(rowData) {
this.selectedRowData = rowData;
}
Table
<div>
<table align="center">
<tr>
<th>
List of Providers
</th>
</tr>
</table>
<table id="thetable" align="center">
<tr>
<th>Application ID</th>
<th>Client Name</th>
<th>Version</th>
<th>API Key</th>
<th>Protected Secret</th>
<th>EDIT/DELETE</th>
</tr>
<tr ng-app="tblRowApp" *ngFor="let prov of providers; let i = index">
<td *ngFor="let col of columns">
<span class="field" *ngIf="i !== index">
{{prov[col]}}
</span>
<span *ngIf="i === index">
<input [(ngModel)]="inputClientName" class="table" value="{{prov[col]}}"
(change)="EditItem(value)" type="text" placeholder="{{prov[col]}}">
</span>
<td>
<span *ngIf="editing && i === index">
<button (click)="save()">Save</button>
</span>
<span *ngIf="i !== index">
<button class="edit" name="editButton" (click)="editToggle(prov); openEditForm()">/</button>
<button class="delete" (click)="deleteRow(i)">x</button>
</span>
</td>
</tr>
</table>
Modal
<!-- The EDITING Modal -->
<div id="editForm" class="modal_edit">
<div class="modal-content_edit">
<span (click)="save()" class="close">×</span>
<h2 style="margin-bottom: 70px">Edit OAuthAppProvider</h2>
<div>
<label style="margin-bottom: 20px">
Client Name:
</label>
<!-- <input id="editClientName" type="text" [value]="selectedRowData?.ClientName"> -->
<input id="editClientName" type="text" [(ngModel)]="selectedRowData.ClientName">
</div>
<div>
<label style="margin-bottom: 20px">
Version
</label>
<!-- <input id="editClientVersion" type="text" [value]="selectedRowData?.Version"> -->
<input id="editClientVersion" type="text" [(ngModel)]="selectedRowData.Version">
</div>
</div>
<div>
<label style="margin-bottom: 20px">
API Key:
</label>
<!-- <input id="editClientAPIKey" type="text" [value]="selectedRowData?.apiKey"> -->
<input id="editClientAPIKey" type="text" [(ngModel)]="selectedRowData.apiKey">
</div>
<div>
<label style="margin-bottom: 20px">
Protected Secret
</label>
<!-- <input id="editClientProtectedSecret" type="text" [value]="selectedRowData?.protectedsecret"> -->
<input id="editClientProtectedSecret" type="text" [(ngModel)]="selectedRowData.protectedsecret">
</div>
<button style="float: right" class="add" (click)="save()">
<h4 style="font-style: bold">Save</h4>
</button>
<button class="cancel" (click)="save()">
<h4 style="font-style: bold">Cancel</h4>
</button>
</div>
</div>
</div>
I have a form that receives a 2D array from the controller and I have generated the table through thymeleaf.
<div class="col-4 offset-4">
<form id="sudokuBoard" method="post" enctype="application/x-www-form-urlencoded" class="form-group text-center">
<div class="container">
<table class="row-eq-height table-striped table-bordered text-center">
<!--/*#thymesVar id="board" type=""*/-->
<tr th:each="row: ${board}">
<td th:each="value: ${row}">
<div th:switch="${value}" class="col-xs-4">
<input name="cellValue" th:case="0" class="content form-control input" style="text-align:center" type="text" pattern="[0-9]*" maxlength="1" value="0" onkeypress="return isNumber(event)" oninput="moveMade()">
<input name="cellValue" th:case="*" class="content form-control input" style="text-align:center;background-color:lightgreen" type="text" th:value="${value}" readonly>
</div>
</td>
</tr>
</table>
<div class="gap-10"></div>
<button type="submit" class="btn btn-outline-primary btn-sm">Check Solution</button>
<button class="btn btn-outline-primary btn-sm" id="btnNewGame">New Game</button>
<button class="btn btn-outline-primary btn-sm" id="solveBoard"> Solve Puzzle</button>
</div>
</form>
</div>
All input fields have the same name and I want to dynamically assign the names through Thymeleaf so they become cellValueX in order to differentiate each cell in order to check the validity of moves entered by the user.
You use the attribute th:name. See this for a list of attributes you can use with Thymeleaf. I don't know specifically from your code what you want to use for X in cellValueX, but possibly something like this?
<tr th:each="row, y: ${board}">
<td th:each="value, x: ${row}">
<input th:name="|cellValue${x.index}_${y.index}|" ... />
</td>
</tr>
I am trying to get an edit form to work with the loop in the php. I have a delete button and it works just fine. The edit form keeps presenting only the first row in the table. In firebug, it shows that all the other forms are there with the correct unique id for each but only the first form shows when edit is clicked on different rows. Can someone help me as to why?
<table id="table_id" class="table table-hover table-striped">
<thead>
<tr>
<th>ID #</th>
<th>Product</th>
<th>Quantity</th>
<th>Price</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<?php
while($row = mysqli_fetch_assoc($search_sql)) {
?>
<tr>
<td id = "clicked"><?=$row['ID']?></td>
<td id = "clicked"><?=$row['Product']?></td>
<td id = "clicked"><?=$row['Quantity']?></td>
<td id = "clicked">$ <?=$row['Price']?></td>
<td>
<button class="btn btn-warning btn-sm" onclick="div_show2(this, '<?=$row['ID']?>'); return false;"><span class="glyphicon glyphicon-edit"></span>Edit</button>
<!-- Modal for the Edit Data Form -->
<div id ="hidden-form2">
<div id = "popupContact">
<form action= "updateData.php" id = "editForm<?=$row['ID']?>" method="POST" name="editForm">
<input type="hidden" name="ID" value="<?=$row['ID']?>">
<div class="form-group">
<label for="product">Product</label>
<input type="text" class="form-control" id="product<?=$row['ID']?>" name="product" value="<?=$row['Product']?>">
</div>
<div class="form-group">
<label for="quantity">Quantity</label>
<input type="text" class="form-control" id="quantity<?=$row['ID']?>" name="quantity" value="<?=$row['Quantity']?>">
</div>
<div class="form-group">
<label for="price">Price</label>
<input type="text" class="form-control" id="price<?=$row['ID']?>" name="price" value="<?=$row['Price']?>">
</div>
<button type="button" class="btn btn-default" onclick="formHide2()">Close</button>
<button type="submit" id="save" class="btn btn-primary">Save changes</button>
</form>
</div>
</div>
<!--End of Edit Form-->
</td>
<td>
<!--Delete Button Form-->
<form method="post" action="deleteData.php">
<button class="btn btn-danger btn-sm" type="submit"><span class="glyphicon glyphicon-trash"></span>Delete</button>
<input type="hidden" id="ID" name="ID" value="<?=$row['ID']?>">
</form>
</td>
</tr>
<?php
}
?>
</tbody>
</table>
//script for calling the edit forms as a modal popup
//Function To Display Popup for Edit form
function div_show2() {
document.getElementById("editForm").reset();
document.getElementById('hidden-form2').style.display = "block";
}
//Function to Hide Popup
function formHide2(){
document.getElementById('hidden-form2').style.display = "none";
}
Your show_div2 function is only showing one element.
document.getElementById('hidden-form2').style.display = "block";
That shows you why you are only seeing the first row.
Update your HTML and Javascript as follows
HTML
<div id="hidden-form2<?=$row['ID']?>">
Javascript
function div_show2(id) {
document.getElementById("editForm"+id).reset();
document.getElementById('hidden-form2'+id).style.display = "block";
}
To avoid having any problems like this in the future, always make sure that your id's are unique.
The first 4 td I see all use the same id name clicked. Try to fix that first and see what happens.
<td class="clicked" id="clickedID<?=$row['ID']?>"><?=$row['ID']?></td>
<td class="clicked" id="clickedProduct<?=$row['ID']?>"><?=$row['Product']?></td>
<td class="clicked" id="clickedQuantity<?=$row['ID']?>"><?=$row['Quantity']?></td>
<td class="clicked" id="clickedPrice<?=$row['ID']?>">$ <?=$row['Price']?></td>
Am developing a application using HTML5 and Spring MVC.
A table is displayed initially and on clicking a row, a form should be populated as dialog with that specific datas of the row filled in form attributes.
So what i need actually is on clicking that row modelArrtibute of that form should be set from jquery or javascript.
<div class="form-group">
<form:label path="smtpServer" for="serialNumber"
class="col-sm-3 col-sm-offset-2 control-label">SMTP Server</form:label >
<div class="col-sm-5">
<form:input path="smtpServer" type="text" class="form-control" id="serialNumber" name="smtpserver"
placeholder="SMTP Server" />
</div>
</div>
<div class="form-group">
<form:label path="portNumber" for="serialNumber"
class="col-sm-3 col-sm-offset-2 control-label">Port
Number</form:label >
<div class="col-sm-5">
<form:input path="portNumber" type="text" class="form-control" id="serialNumber"
placeholder="Port Number" />
</div>
</div>
<div class="col-sm-offset-4">
<p>
<h5>
<form:checkbox path="issmtpEnabled" value="1"/>Enable SMTP Authentication
</h5>
</p>
</div>
<div class="form-group">
<form:label path="fromAddress" for="serialNumber"
class="col-sm-3 col-sm-offset-2 control-label">From
Email Address</form:label >
<div class="col-sm-5">
<form:input path="fromAddress" type="text" class="form-control" id="serialNumber"
placeholder="From Email Address" />
</div>
</div>
<div class="modal-footer">
<input type="submit" class="btn btn-primary" value="Apply" />
</div>
</form:form>
TABLE
<table class="table table-hover">
<thead>
<tr class="active">
<th>SMTP Server</th>
<th>Port Number</th>
<th>Sender Email</th>
<th>Use SSL/TLS Secure Connection</th>
</tr>
</thead>
<tbody>
<c:forEach var="PT"
items="${alertSettingsManagement.emailSettingsAlertList}">
<tr id="${PT.id}" class="emailSettingsRow">
<td>${PT.smtpServer}</td>
<td>${PT.portNumber}</td>
<td>${PT.fromAddress}</td>
<td>${PT.issslEnabled == 1 ? 'Yes' : 'No'}</td>
</tr>
</c:forEach>
</tbody>
</table>
JQUERY
$('.emailSettingsRow').click( function() {
//want to set modelAttribute here
var row = $(this).find('td:first').text();
$('#emailModal').modal('show');
});
You can not change modelAttribute of a spring tag.
Why because at the time of the rendering the response spring's tag will be converted as normal tag in HTML, and it will not contain modelAttribute any more, modelAttribute will be converted as name and id (i.e. name of modelAttribute will be given to both name and id attributes in the HTML form tag).