I'm working on an invoice system. By default it loads with 1 row to input items. If a administrator wants to add additional items they may click a link which executes a JavaScript to add additional rows. This is working great for me.
I submit the form using POST method to the same page. If a user added additional rows using the JavaScript, it loads with the previous amount of rows as when the page was submitted. To do this I used PHP $table_rowcount = count( $_POST["item_details"] );. Just FYI, the {$table_rowcount} isn't a syntax error as I'm using a template engine.
Everything works great to this point. Administrators can add additional rows to input data and when the page is submitted to itself it loads those number of rows. Perfect. However this is where the problem comes in.
What I need to do is use a method of sticky forms so if there is an error, the page will reload (as it currently does) with variables assigned to each inputs value="" so the administrator wont have to re-enter all the details.
The problem is I do not know how to do this with a combination of JavaScript and PHP. I need to some how get the current number of the row using the for() loop in JavaScript and then use that number in a PHP variable as seen in the tbody of the HTML below for the default 1st row.
Basically if it is the 3rd row and the for() loop will show it as var i = 2 (since array starts at 0), how can I use JavaScript to help me call the PHP variable {$item.2.details}.
I'm assuming I need to use AJAX for this? However I do not know AJAX. I'm hoping someone could assist me with this so myself and others can learn how this is properly done.
Question simply put
How can I use a JavaScript variable to select a PHP array in a HTML file?
JavaScript:
<script type="text/javascript">
window.onload = function loadRows() {
for( i = 1; i < {$table_rowcount}; i++ ) {
// Set table id.
var table = document.getElementById("invoice_items");
// Count rows.
var rowCount = table.rows.length;
// Row to insert into table.
var row = table.insertRow(rowCount);
// Cells to insert into row.
var cell1 = row.insertCell(0);
var cell2 = row.insertCell(1);
var cell3 = row.insertCell(2);
// Current Row.
var rowNumber = rowCount + 1;
// Data in cells.
cell1.innerHTML = "<textarea name=\"item_details[]\"></textarea>";
cell2.innerHTML = "<input type=\"text\" name=\"item_quantity[]\" value=\"\" />";
cell3.innerHTML = "<input type=\"text\" name=\"item_price[]\" value=\"\" />";
}
}
</script>
HTML:
<form method="post" action="index.php?do=invoices_add">
<label class="label">
<div class="left">
Invoice Items
</div>
<table id="invoice_items" cellpadding="0" cellspacing="0" border="0">
<thead>
<tr>
<td>
Item Details
</td>
<td>
Quantity
</td>
<td>
Price
</td>
</tr>
</thead>
<tbody>
<tr>
<td>
<textarea name="item_details[]">{$item.0.details}</textarea>
</td>
<td>
<input type="text" name="item_quantity[]" value="{$item.0.quantity}" />
</td>
<td>
<input type="text" name="item_price[]" value="{$item.0.price}" />
</td>
</tr>
</tbody>
</table>
+ Add Item
</label>
<br/>
<label class="label">
<input type="submit" value="submit" name="submit" />
</label>
</form>
Just to make sure I understand correctly: You have users who enter new rows, and if there is an error, their newly entered rows are lost, along with the information they typed. Based on that:
My first instinct is to post the form via AJAX, but if you want to go the PHP route, I would recommend researching session based flash messages. You can use session data to send back the values which have already been input, along with additional error messages. I have previously set flash data as one object with property names such as "errors". "errors" would be an array of error messages, but you can also set a property called "additionalItem" and add the newly created items by the user. Then add these to your row iteration in the PHP, avoiding the necessity of using JavaScript to build the rows.
$flashData = Array("additionalItem" => Array());
for ($_PARAMS["addedRows"] as $row) {
$flashData["additionalItem"] []= Array(
"rowDetails" => $row['details'],
"rowQuantity" => $row['quantity'],
"rowPrice" => $row['price']
)
}
I would also recommend client side validation where possible. However, I also understand other non-client-side errors can arise.
If you have to do it in JavaScript then I think your best option here is to expose the PHP array you want to iterate over and store it as a JavaScript variable (so either way you will probably need to set flash data).
<script type="text/javascript">
// I renamed item to items as I am assuming you have an array.
// This would preferably be an array of items from the server.
var items = {$items},
table = document.getElementById("invoice_items");
window.onload = function loadRows() {
for (i = 1; i < items.length; i++) {
var rowCount = table.rows.length,
row = table.insertRow(rowCount),
cell1 = row.insertCell(0),
cell2 = row.insertCell(1),
cell3 = row.insertCell(2),
rowNumber = rowCount + 1;
cell1.innerHTML = "<textarea name='item_details[]'>" +
items[i].details + "</textarea>";
cell2.innerHTML = "<input type='text' name='item_quantity[]'
value='' />";
cell3.innerHTML = "<input type='text' name='item_price[]'
value='' />";
}
}
</script>
I hope this helps and that I did not misunderstand the question :)
Related
I've the following PHP code :
//some code
$query = "SELECT * from store_00_transfers";
$result = mysql_query($query);
while($row = mysql_fetch_assoc($result)){
echo "<tr><td align=center width=19%>{$row['item_no']}</td><td align=center><input type='text' value='{$row['qty']}' name='cqty[]' readonly ></td><td align=center><input type='number' min='0' name='nqty[]' onChange='check_not_large(this.value);' value='0'></td></tr>";
}
//rest of code
Now, as you can see there is a function inside onChange. That function is meant to check whether the number inserted by user for new qty should not exceed old qty (I use this method to transfer qty's from one store to another).
Please also note I've assigned a name cqty[] and nqty[] inside input fields.
I have javascript code :
<script>
function check_not_large(res) {
var old = document.getElementsByName("cqty[]");
var type= document.getElementsByName("nqty[]");
for(var i = 0; i < old.length; i++) {
if(type[i].value > old[i].value){
alert('Error : Qty Inserted should be Less than QTY found in Store !');
alert(type[i].value);
alert(old[i].value);
return type[i].value = 0;
}
}
}
</script>
I've used alert in javascript to test if correct values are collected and it seems that it works as needed, but NOT for validating if new qty is more than old qty, it seems that it works only for the first 4 rows then it stucks and starts validating the new qty in any row WITH the first old qty row .. I hope this makes sense to you.
Any alternative or suggestion will be appreciate it.
Thanks Guy
One problem with your code is that everytime you input value onto nqty and on onchange, the function check_not_large function gets called and it not only validating current row nqty and cqty values but also all the previous nqty and cqty row values.If your purpose is to check only if current row nqty value greater than cqty value, a much neater code will be to give some unique id value to each nqty and cqty elements.The code for the same can be optimized as given below.
PHP Code
$query = "SELECT * from store_00_transfers";
$result = mysql_query($query);
while($row = mysql_fetch_assoc($result)){
echo "<tr>
<td align=center width=19%>{$row['item_no']}</td>
<td align=center>
//instead of name attribute you can give an id here and also assuming $row['item_no'] is unique
<input type='text' value='{$row['qty']}' id='cqty{$row['item_no']}' readonly >//assuming item_no is unique
</td>
<td align=center>
//instead of name attribute you can give an id here and also assuming $row['item_no'] is unique
<input type='number' min='0' id="nqty{$row['item_no']}" onChange='check_not_large({$row['item_no']});' value='0'>
</td>
</tr>";
}
Javascript Code
<script>
function check_not_large(item_no) {
var cqtvVal=document.getElementById("cqtv"+item_no).value;
var nqtvVal=document.getElementById("nqtv"+item_no).value;
//checking if typed value is greater than old value
if(nqtvVal>cqtvVal)
{
//write whatever code you want to do like give a warning message or make the nqtv value reset to zero etc
}
}
</script>
I have
var table = "<tr><td><input type='hidden' class='hid_id' value='"+id+"' /> "+id+
"</td><td>"+document.getElementById("name_"+id).value+
"</td><td>"+document.getElementById("price_"+id).value+
"</td><td><input type='text' id='qua_"+id+
"' value='1' disabled='disabled' /></td><td><button>more</button></td></tr>";
to add to table when user click Addbutton
<table id="tbForm1" border="1">
<tbody>
</tbody>
</table>
But I want store it (var table) and assign to other one variable and all row when user click add. How I can ?
Edit
I don't know syntax of JavaScript but if it's PHP, I mean:
$ba .= 'a';
if(click=='add'){
$ba .='b';
}
echo $ba; //outup ab
As Vohuman mentioned use that function to create dynamic row with an Id and use below to add that row to another table.
var x =document.getElementById("tbForm1").getElementsByTagName('tbody');
x.innerHTML = generateRow(id);
Also refer to this to add subsequent rows
How to insert row in HTML table body in javascript?
I want to pass all datas stored in the table in my template. The table is growing by user's choices. My project is about a food ordering system and what i'm trying to do is, when user adds menus to its basket, and make an order, how can i pass the values to my view to save them to my db. I know about forms but my table is not static.. I can't imagine how it will be big.
My add row func:
$(document).ready(function(){
var sum = 0;
$(document).on('click', '.myaddButton', function() {
var $foodname = $(this).closest("tr") // Finds the closest row <tr>
.find(".nr") // Gets a descendent with class="nr"
.text();
var $foodprice = $(this).closest("tr") // Finds the closest row <tr>
.find(".rn") // Gets a descendent with class="nr"
.text();
$('#carttable').prepend("<tr class='danger' id ='myTableRow'><td>"+$foodname+"</td><td class='price'>"+$foodprice+"</td> <td> <button class='deletebutton btn btn-danger' type='button'> <span class='glyphicon glyphicon-trash'></span> </button> </td></tr>");
});
$(document).on('click', '.deletebutton', function() {
$('#myTableRow').remove();
//$('.price').each(function() {
// var $price = $(this);
//console.log($price);
//sum += parseInt($price.context.innerHTML);
//});
//$('#total').html(sum);
//sum = 0;
});
});
</script>
My Table
<table border="1" class="table" id="menutable" name="menutable">
<tr class="danger">
<tbody>
{%for a in list%}
<tr class= {% DoRandom %}>
<td><b> {{a.name}}</b> </td>
<td class="nr">{{a.description}}</td>
<td class="rn"><p>{{a.price}}</p></td>
<td id="addbutton" > <button class="myaddButton btn btn-success" type="button">
<span class="glyphicon glyphicon-plus"></span> </button> </td>
</tr>
{%endfor%}
</tbody>
</table>
If you want to use little bit higher technique of form processing, then you should look at question Making a Django form class with a dynamic number of fields...
Second "simple" solution is to add hidden input values (which will contain food_id and quantity) together with name and price (during prepend), and wrap your table in form. E.g
var $food_id = ... // some how get food id
var $quantity = 1; //1 for now, but you can add increase and decrease functionality
...
// in prepend text
... + "<input type='hidden' name='food-" + $food_id + "' value='" + $quantity + "' >" + ...
And in your template you should add <form> before <table> (and </form> after </table> tags), or use serialize if you are using ajax.
Third solution is to use JS object as cart, and encode it to Json before form submittion. E.g.
//in your javascript global scope
var js_food_cart = {};
....
// then on click
$food_id = ... ;// yes, again you need food id
$quantity = 1; // or something elese
js_food_cart[$food_id] = $quantity;
....
// then somwhere in before submit, code assumes that you have form with my_cart hidden input
$('input[name=my_cart]') = JSON.stringify(js_food_cart);
Then in view you should parse your json value of my_cart input. In template you should add form with hidden field to pass cart value.
This approach more convenient if you will implement ability to increase/decrease quantity of food.
Thanks, i resolved the issue by casting my food objects to json and post by AJAX post.
so this function seems to be confusing me.
echo"
<td style='font-size:12px;width:150px;'><div style=\"overflow-y:auto; max-height:250px; width:200px;\">
{$row['Notes']} </div><br /><center><br />
<button onclick=\"myFunction('{$row['ID']}','$rowID')\">Add Note</button>
<form action=\"http://calls.fantomworks.com/functions/notes.php\" id='notesForm' name='notesForm' method='post'>
<input type='hidden' id='notesID' name='notesID' />
<input type='hidden' id='rowID' name='rowID'/>
<input type='hidden' id='notes' name='notes' />
</form>
</center>";
Calls this javascript
<script language="JavaScript" type="text/javascript">
function myFunction(ID,rowID)
{
var x;
var ID = ID;
var rowID = rowID;
var note = prompt("Customer Note","Write your customer note here...");
if (note != null) {
document.getElementById("notes").value = note;
document.getElementById("notesID").value = ID;
document.getElementById("rowID").value = rowID;
document.getElementById("notesForm").submit();
}
else{
return false;
}
}
</script>
and ends up at this php page
$notesID = $_POST['notesID'];
$rowID = $_POST['rowID'];
$note = $_POST['notes'];
//Redirect to browser
header("Location: ./index.php#row_$rowID");
The only problem is that the rowID does not seem to be making it through and generates links ending like "index.php#row_"
I can't make sense of why rowID isn't coming through but NotesID and notes are.
As you can see from the debug the value is there.
Thanks for any thoughts or suggestions!!
The script at "http://calls.fantomworks.com/index.php" is being POSTed to by your javascript function - thus the variable that you seek ought to be available through the $_POST global.
Try changing
header("Location: ./index.php#row_$rowID");
To
header("Location: ./index.php#row_{$_POST['rowID']}");
Incidentally, the three variables you define in the javascript function seem redundant and could be removed by the looks of things, namely:-
var x;
var ID = ID;
var rowID = rowID;
Have had a closer look since posting original ( and hadn't noticed the assignment of posted vars by the #OP ) - there are hundreds of forms on the page in question - same IDS used from row to row to row. IMHO - this is definitely NOT the way forward - You could have just one form for "Add Note" as you dynamcally set the value by clicking the button. It does appear that the relevant vars ( rowID etc ) are being set and assigned to the button that calls the javascript so theoretically you could have just one form that is used to post to "notes.php" but have this button on each row.
In terms of a general critique / suggestions
The page is very slow to load - due in part to there being hundreds of complex table row layouts, and by the looks of things a form for every button - then there are the images which themselves are fullsize but could really be ( and should be ) thumbnails. The number of forms could be drastically reduced if each button were to dynamically assign the variables like the one in the question above.
First time using ajax. Have successfully progressed through a number of teething problems, so far with happy results. However now is a more confusing one specific to one particular input field nested within a table - there is a good reason for that.
First the html:
<table id="speakersName" style="width: 100%; height: auto;">
<tbody><tr class="activity_row">
<td class="right" style="width: 190px;">Name of Speaker:</td>
<td><input type="text" id="input_3_1" name="input_3_1" id="input_3_1" placeholder="Name of Speaker" value="<?=$input_3_1?>" required></td>
<td><input type="button" name="button2" id="button2" value=" +1 " class="button" style="width: auto !important; margin: 5px;"></td>
</tr>
<tr>
<td class="center" colspan="3"><input type="hidden" name="MAX_FILE_SIZE" value="5632000">
<label for="file">Filename:</label> <input type="file" name="file" id="file">
<input class="button" style="width: 70px; margin-top: 12px;" type="submit" name="submit" value="Upload"></td>
</tr></tbody>
</table>
We can fairly much ignore the section containing the file upload. I just wanted to be clear about the entire table structure.
The .js file that is included in the head contains this relevant code:
function doSend_3_1() {
$.post('./post.4.ConSupAp.php?appID=' + (appID) + '&ident=input_3_1', $('#input_3_1').serialize());
}
$("document").ready(function() {
$("#input_3_1").blur(doSend_3_1);
})
Which ajax's the data entered into the text input field over to this bit of php:
// include the funcky stuff
include './conf/Funcs.php';
include './conf/DBconfig.php';
// GET the constants
$appID = $_GET['appID'];
$ident = $_GET['ident'];
if(($ident) == "input_3_1") {
$userInput = $_POST['input_3_1'];
if(($userInput == "") || ($userInput == " ") || ($userInput == NULL)) { $userInput = NULL; }
try {
$stmt = $conn->prepare("UPDATE $database.app_ConSupAp SET `nameOfSpeakers` = :userinput, `lastModified` = :time WHERE `appID` = :appid");
$stmt->bindParam(':userinput', $userInput, PDO::PARAM_STR, 128);
$stmt->bindParam(':time', time(), PDO::PARAM_INT, 11);
$stmt->bindParam(':appid', $appID, PDO::PARAM_INT, 11);
$stmt->execute();
} catch(PDOException $e) { catchMySQLerror($e->getMessage()); }
}
Which happily drops in the text that the user typed into the initial text input field, soon as they click out of it. This technique is being used across the form successfully.
True I don't yet have a success or error message coming back to the user facing page, but I'll get onto that after I've sorted this query out. One thing at a time, right? :)
Ok so now I'll show what makes the particular table input (the one above the file upload ) a little more complicated. In the head of the html facing page, I have also got the following code, within a tag:
$(window).load(function() {
// trigger event when button is clicked
$("#button2").click(function() {
// add new row to table using addTableRow function
addTableRow($(this),$("#speakersName"));
// prevent button redirecting to new page
return false;
});
// function to add a new row to a table by cloning the last row and incrementing the name and id values by 1 to make them unique
function addTableRow(btn,table) {
// clone the last row in the table
var $tr = btn.closest($("tr")).clone(true);
var num; // Current unique field number
// Clear the input fields (that are not the button)
$tr.find(":not(:button)").val("");
// get the name attribute for the input field
$tr.find("input").attr("name", function() {
// break the field name and its number into two parts
var parts = this.id.match(/(\D+)(\d+)$/);
num = parts[2]; //Get the number for later
// create a unique name for the new field by incrementing the number for the previous field by 1
return parts[1] + ++parts[2];
// repeat for id attributes
}).attr("id", function() {
var parts = this.id.match(/(\D+)(\d+)$/);
return parts[1] + ++parts[2];
});
btn.remove();
num++;
// append the new row to the table
$(table).find(".activity_row:last").after($tr);
};
});
And this function works wonderfully on it's own, it pops up new table rows for other input, in a nice unlimited manner. I've used a variation on this once before (for which it was originally written for) but that was not utilising ajax. This version works as expected for the initial input value, but I believe I need some sort of JS foreach function to arrange each of the additional new input text fields into one value, separated by a delimiter such as ^ so that I can break them up in the php and count them there with an explode and foreach.
jQuery is being used.
This is where I'm lost as I do not know how to achieve this. Help warmly received. :)
I carefully study your job at http://jsfiddle.net/k3dj214k/2/
Now, I will try explain all the steps to fix errors:
The form page html:
<form id="ConSupAp_section_3" name="ConSupAp" action="./post.4.ConSupAp.php" method="post" enctype="multipart/form-data"><!-- edited by kazumov#gmail.com -->
<input type="hidden" name="token" value="3e57334833283e22579f77e3a1ade083edf637bd3f4ab8009bbf1f4d7f517fde">
<input type="hidden" name="uID" value="1">
<input type="hidden" name="uaID" value="1">
<input type="hidden" name="appID" value="1">
<input type="hidden" name="ident" value="input_3_1"><!-- edited by kazumov#gmail.com -->
<h2 style="margin: 0 auto 20px;">Conference Support Application - Section 3</h2>
<table id="speakersName" style="width: 100%; height: auto;">
<tbody>
<tr>
<td colspan="3" style="padding: 30px;"><span class="h3">3.1</span>Please list names of guest speaker(s). Use the <strong>+1</strong> button to add addtional speakers.</td>
</tr>
<tr class="activity_row">
<td class="right" style="width: 190px;vertical-align:top">Name of Speaker:</td>
<td id="speakers_list"><!-- edited by kazumov#gmail.com -->
<!--<input type="text" name="s" placeholder="Name of Speaker" value="" required>--><!-- edited by kazumov#gmail.com -->
</td>
<td>
<input type="button" id="btnAddSpeaker" value=" +1 " class="button" style="width: auto !important; margin: 5px; vertical-align:bottom"><!-- edited by kazumov#gmail.com -->
</td>
</tr>
</tbody>
</table>
</form>
I added one hidden input and delete text input. The form tag id should be renamed to ConSupAp_section_3.
The app_ConSupAp.js editions:
Kill doSend_3_1() function
// edited by kazumov#gmail.com
//function doSend_3_1() {
// $.post('./post.4.ConSupAp.php?appID=' + (appID) + '&ident=input_3_1', $('#input_3_1').serialize(), function(data) {
// $("#errorText_3_1").html(data.errorText_3_1);
// $("#resultImg_3_1").html(data.resultImg_3_1);
// }, 'json');
//}
Kill whole module for names manipulation:
// edited by kazumov#gmail.com
// // trigger event when button is clicked
// $("#button2").click(function() {
// // add new row to table using addTableRow function
// addTableRow($(this), $("#speakersName"));
// // prevent button redirecting to new page
// return false;
// });
//
// // function to add a new row to a table by cloning the last row and incrementing the name and id values by 1 to make them unique
// function addTableRow(btn, table) {
// // clone the last row in the table
// var $tr = btn.closest($("tr")).clone(true);
// var num; // Current unique field number
// // Clear the input fields (that are not the button)
// $tr.find(":not(:button)").val("");
// // get the name attribute for the input field
// $tr.find("input").attr("name", function() {
// // break the field name and its number into two parts
// var parts = this.id.match(/(\D+)(\d+)$/);
// num = parts[2]; //Get the number for later
// // create a unique name for the new field by incrementing the number for the previous field by 1
// return parts[1] + ++parts[2];
// // repeat for id attributes
// }).attr("id", function() {
// var parts = this.id.match(/(\D+)(\d+)$/);
// return parts[1] + ++parts[2];
// });
// btn.remove();
// num++;
// // append the new row to the table
// $(table).find(".activity_row:last").after($tr);
// };
append the script page with:
// ---------------------------------------------------
// code addition for phase (3) "Speakers" of "Guests"
// edited by kazumov#gmail.com
// ---------------------------------------------------
$(document).ready(function() {
function addSpeakerNameField() {
var $txtInput = $("<input type=\"text\" name=\"speakers[]\" placeholder=\"Name of Speaker\" value=\"\" required />");// extended notation to create input element, 'id' is not nesessary
$("#speakers_list").append($txtInput);
$txtInput.blur(function(){// change value event
$.post(
"post.4.ConSupAp.php", // your address of page is different, i made temporary php page to debug
$("#ConSupAp_section_3").serialize(),// get all form values
function(data) {
// actually, your html have no tags with id "errorText_3_1" and "resultImg_3_1"
$("#errorText_3_1").html(data.errorText_3_1);// not working
$("#resultImg_3_1").html(data.resultImg_3_1);// not working
},
'json');
});// end of blur()
}
addSpeakerNameField();// the first field
$("#btnAddSpeaker").click(function() { // add one more field
addSpeakerNameField();
});
});
// end of edition by kazumov#gmail.com
As you can see, the important editions are:
a) you should generate all the input text fields from code, because it will create the whole sending routine for all the fields in one place;
b) you should naming the text fields in html like name="speaker[]", because it will create array after serialization;
c) you should adding hidden inputs inside the form, if you want to send static values;
d) i recommend you delete all over-navigation:
and rename the tabs:
Finally, in post.4.ConSupAp.php you will reach the names:
$speakers = $_POST["speakers"];// returns array
And you should to add the header to the post.4.ConSupAp.php
header("Content-type: application/json");
if you expecting the data.errorText_3_1 and data.resultImg_3_1 output to the form.
This looks like a situation where you have a jquery event you would like to bind to a number of elements, but not all of those elements have been created when the event - blur() - is bound.
You can bind events to higher DOM element and use the following syntax to bind events to new elements as they are created:
$("body").on("blur", "input.some_class_name", do_send);
When do_send() is called, "this" will be defined as the element where the event was generated, so you can identify which element needs to be posted:
function do_send(e) {
// "this" is the dom element
var the_id = $(this).attr('id');
var value = $(this).val();
// post away!
}