HTML select form - how to trigger "dynamically generated option" - javascript

I have a HTML form along with a script, which automatically triggers the default "option" (option values hard-coded).
I also have a stand-alone script, which creates the choices of "options" dynamically, based on a specific column values in MySQL.
Problem: When selecting from the drop-down list from the dynamically generated list, it does not "trigger" anything (nor manual, nor automatic, manual would be just fine in this case)
The form which auto-triggers the default "option":
<form>
<select name="fruit" onchange="showFruit(this.value)">
<option>Choice:</option>
<option value="1">Yellow Fruit</option>
<option value="2">Red Fruit</option>
</select>
</form>
<script>
window.onload = function () {
var el = document.getElementsByName('fruit')[0];
el.value = 1; //Set default value
el.onchange(); //trigger onchange event
}
function showFruit(val) {
alert(val);
}
</script>
And the code which generates the dynamically created "options" list from a specific MySQL column:
<? $connect = mysqli_connect('localhost', '*', '*', '*');
$sql="SELECT DISTINCT(fruit_name) AS fruit_name FROM fruit ORDER BY fruit_name ASC";
$result = mysqli_query($connect, $sql);
if(mysqli_num_rows($result) > 0){
$select= '<select name="select">';
while($rs = mysqli_fetch_array($result)){
$select.='<option value="'.$rs['id'].'">'.$rs['fruit_name'].'</option>';
}
}
$select.='</select>';
echo $select;
How can I have the onload function to attach to the dynamically generated "options" list? I don´t need any hard-coded "options" at all. Just the dynamically generated one. And when I select from the list, I would like to get a response/reaction of any sorts.

I would probably keep them separated for clarity of purpose and include your template file into your logic file that does the work of formatting the data for the page. By gating the select menu behind a conditional you can make sure it only renders when you have data to put in it.
I formatted this in a particular way, that you don't need to follow but it is fairly good practice to pick a consistent style and stick to it throughout. I also updated your usage of the mysqli class to the object oriented approach. Be aware that in this case you aren't vulnerable to sql injection while using Mysqli->query, but if you are planning on taking user input you should study prepared statements to prevent sql injection.
practically I use the <?= ?> operator in the template file this is a shorthand for <?php echo '';?>, use of <? as your php file opener is discouraged <?php is standard.
PHP also allows for short open tag <? (which is discouraged since it is only available if enabled using the short_open_tag php.ini configuration file directive, or if PHP was configured with the --enable-short-tags option).
When mixing PHP and HTML usually its more clear (opinion!) to use the alternative if and loop syntax where possible (the <?php if () : ?> <?php endif; ?> rather than <?php if () { ?> <?php } ?>) but both work
Logic File
<?php
// page.php
$display = false;
$mysqli = new mysqli(HOST, USER, PASSWORD, SCHEMA);
$sql = "SELECT DISTINCT(fruit_name) AS fruit_name FROM fruit ORDER BY fruit_color ASC";
$result = mysqli->query($sql);
// not sure this is necessary in this particular case, but
if ($result->num_rows > 0) {
$display = true;
$select = []
while ($row = $result->fetch_assoc()) {
$select[] = "<option value=\"{$row['id']}\">{$row['fruit_name']}</option>";
}
}
include 'htmlfile.php';
?>
Template File
<!-- htmlfile.php -->
<form>
<?php if ($display) : ?>
<select name="select">
<?= implode('', $select) ?>
</select>
<?php endif; ?>
</form>
<script>
window.onload = function () {
var el = document.getElementsByName('fruit')[0];
el.value = 1; //Set default value
el.onchange(); //trigger onchange event
}
function showFruit(val) {
alert(val);
}
</script>

Related

Generating SELECT OPTIONS with inline code?

<body>
<H1>4a</H1>
<form action="hw4b.php" method="post">
<?php
$con = mysqli_connect("localhost","[credential]","","[credential]")
or die("Failed to connect to database " . mysqli_error());
?>
<select name="id" value="id">
<script>
for (x=1;x<=101;x++)
{
document.write("<option value="+x+">"+
<?php echo mysqli_query($con, "SELECT LASTNAME FROM CUSTOMERS WHERE CUSTOMERID == "+x+";")?>
+"</option>");
}
</script>
</select>
<input type="submit" value="SEND IT">
</form>
</body>
So this should put the corresponding LASTNAME into the select, but it just fills every row with "NaN". I'm sure this is some stupid minor error, but I've been staring at it too long.
you should query the results of mysqli_query
do something like this:
<select name="id" value="id">
<?php
$query = mysqli_query($con, "SELECT LASTNAME FROM CUSTOMERS WHERE WHERE CUSTOMERID >=1 and CUSTOMERID <= 101 ;");
while ($row = mysqli_fetch_array($query))
echo "<option id='".$row['LASTNAME']."'>".$row['LASTNAME']."</option>";
?>
</select>
notes:
no need for javascript usage
please escape the query parameter
id of the option is the value that will be sent to the server, makes more since to send LASTNAME
avoid using a query at a loop
Note that your for cycle is in javascript (between <script> tags), yet you try to fill in some data in php.
Everything in PHP happens on server side, i.e. is interpreted, packed into a http response and returned to the client, where it is unpacked and javascript is executed.
You need to either put both into javascript, or both into php.
<select>
<?php
for ($i = 0; $i < 100; i++){
///make some select here
echo "<option value="$i"> ...output the select </option>"
}
?>
</select>
This way, all options are generated on server side and transferred to client as text
<select>
<option value="0">...</option>
<option value="1">...</option>
...
Other option is to export the database data into javascript, and then access it in javascript.
<script>
//or perhaps better
var myOtherData = <?=json_encode($somePHPData)?>;
</script>
//now you can use for loop with document.write and one of the variables you exported...
You need to be very careful and sure which execution happens on server, and which on client side.
There are several issues I think. You are using a comparison operator in the SELECT statement, it should just be =, not ==. Also, mysqli_query returns a mysqli_result, not a value like "Johnson" for LASTNAME. And, maybe most importantly, it doesn't make sense to do this with javascript since you're writing the values to the document before sending it to the browser anyway.
The code should look something like this (not tested)
<select name="id" value="id">
<?php
$query = 'SELECT LASTNAME, CUSTOMERID FROM CUSTOMERS WHERE CUSTOMERID >= 1 AND CUSTOMERID <= 101 ORDER BY CUSTOMERID ASC';
$result = mysqli_query($con, $query);
if (!$result) {
echo 'some error handling';
} else {
while ($row = $result->fetch_assoc()) {
echo '<option value="' . $row['CUSTOMERID'] . '">' . $row['LASTNAME'] . '</option>';
}
}
?>
</select>

Filtering dropdown based on another dropdown selection

I have 2 separate dropdown lists. I need to get each dropdown to filter each other. Every example I have seen so far is an example for dropdowns that have the options hard-coded in. Mine uses a query to populate the options.
So how could I correctly have each dropdown menu filter each other?
Here is my HTML for the dropdowns on index.php:
<select id="collector" onchange="showUser(this.value)">
<option value="" selected disabled>Collector Name</option>
<?php foreach($collect->fetchAll() as $name) { ?>
<option class="<?php echo $name['Collector Name'];?>" value="<?php echo $name['Collector Name'];?>"><?php echo $name['Collector Name'];?></option>
<?php } ?>
</select>
<select id="date" onchange="showUser(this.value)">
<option value="" selected disabled>Bill Date</option>
<?php foreach($bill_date->fetchAll() as $date) { ?>
<option class="<?php echo $date['Date'];?>" value="<?php echo $date['Collector Name'];?>"><?php echo $date['Date'];?></option>
<?php } ?>
</select>
Code that runs each time the dropdown is changed in script tags on index.php:
function showUser(str) {
if (window.XMLHttpRequest) {
// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest();
}
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("txtHint").innerHTML = this.responseText;
var newTableObject = document.getElementById('billing_table');
sorttable.makeSortable(newTableObject);
}
}
// ---- Gets value of collector dropdown selection -----
var e = document.getElementById("collector").value;
$.ajax({
type: 'GET',
url: 'index.php',
data: e,
success: function(response) {
console.log(e);
}
});
// ---- Gets value of the current selection in any of the dropdowns ----
xmlhttp.open("GET","dropdown-display.php?q="+str,true);
xmlhttp.send();
document.getElementById('billing_table').style.display = 'none';
}
$(document).ready(function(){
var $select1 = $( '#collector' ),
$select2 = $( '#date' ),
$options = $select2.find( 'option' );
$select1.on( 'change', function() {
$select2.html( $options.filter( '[value="' + this.value + '"]' ) );
}).trigger( 'change' );
});
Query on my index.php page:
$collector = "SELECT [Collector Name]
FROM [vSpecial_Billing]
Group By [Collector Name]";
$billdate = "SELECT [Collector Name], [Date]
FROM [vSpecial_Billing]
Group By [Collector Name], [Date]";
I don't want to send the value to my dropdown-display.php page since my queries that populate the dropdowns are on my index.php page. However, if I put the value variable in the query, then it runs that query on load before a collector selection can be made and my bill date dropdown will then not be populated.
EDIT:
I changed the value in the options for the date dropdown to Collector Name instead of Date
I also added the $(document).ready(function() at the end of the middle block of code
I updated the queries that I am using
It filters correctly now, however, on page load, the bill date is unable to selected. It is not populated with any rows. How can I change this?
Also, when I filter it, it defaults to the last date on the list. How can I get it to default to a hardcoded value such as "Date" and then the user can select from the filtered values?
I wrote up a test case, using some example data, and made sure this works. Its a rough example, but I believe its doing what you need. With a lot less cruft in the works. I'm sorry, but I used full jquery, because I cannot be bothered to do long-hand javascript anymore haha (plus I couldn't really follow what you had going on in there).
There will need to be two files: index.php and index-ajax.php (for clarity)
index.php brief:
// note: these do not need to be in prepared statements (theres no variables inside)
$collect = $db->query("SELECT DISTINCT [Collector Name] FROM [vSpecial_Billing]");
$names = $collect->fetchAll();
$billdate = $db->query("SELECT DISTINCT [Date] FROM [vSpecial_Billing]");
$dates = $billdate->fetchAll();
?>
<form id="testForm" action="">
<select id="collector">
<option value="" selected="selected" disabled="disabled">Collector Name</option>
<?php foreach($names as $name) { ?>
<option class="choice" value="<?php echo htmlspecialchars($name['Collector Name']);?>"><?php echo $name['Collector Name'];?></option>
<?php } ?>
</select>
<select id="date">
<option value="" selected="selected" disabled="disabled">Bill Date</option>
<?php foreach($dates as $date) { ?>
<option class="choice" value="<?php echo $date['Date'];?>"><?php echo $date['Date'];?></option>
<?php } ?>
</select>
<input type="button" id="clearchoices" name="clearchoices" value="Clear Choices" />
</form>
Some things to note in the above:
You only need to select by DISTINCT. No need to do GROUP BY to get all unique names, or all unique dates.
I put the results of fetchAll into variables, out of habit, but you can move them into the foreach if you wish.
I removed the class defines you had, because a class with spaces in it (in the case of a Collector Name) can be buggy.
The Clear Choices button is just an example of how to reset those selects after they get filtered and filtered beyond what you can select.
This is the javascript portion (it goes in index.php before or after your form, or in the head):
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script language="Javascript" type="text/javascript">
$(document).ready(function(){
$("#collector").change(function(e){
$.post('index-ajax.php',{filter:'Name',by:$(this).val()},function(data){
$("#date .choice").hide();
$.each(data, function(key,row) {
// $("#date option[value='"+ row.item +"']").show();
$("#date option").filter(function(i){
return $(this).attr("value").indexOf( row.item ) != -1;
}).show();
});
},"JSON");
});
$("#date").change(function(e){
$.post('index-ajax.php',{filter:'Date',by:$(this).val()},function(data){
$("#collector .choice").hide();
$.each(data, function(key,row) {
// $("#collector option[value='"+ row.item +"']").show();
$("#collector option").filter(function(i){
return $(this).attr("value").indexOf( row.item ) != -1;
}).show();
});
},"JSON");
});
$("#clearchoices").click(function(e){ e.preventDefault();
$("#collector .choice").show(); $("#collector").val('');
$("#date .choice").show(); $("#date").val('');
});
});
</script>
That block needs a lot of explaining, because I took all your long-hand javascript and packed it into jquery.
Each select has its own handler event for when it changes.
Each select does its own post ajax, with a different variable define to filter on.
After the ajax returns, it hides all options in the OTHER select. Then enables all options which are returned by the json data of the ajax call. This could be handled differently, but I wanted to present one way of doing it.
A key thing is setting "JSON" for the return handler of the .post() methods. You'll see why in index-ajax.php.
And now the index-ajax.php:
if (isset($_POST['filter']) and isset($_POST['by'])) {// sanity check
$results = array();
if (!empty($_POST['by'])) {
// these _DO_ need to be in prepared statements!!!
if ($_POST['filter'] == 'Name') { $sql = "SELECT DISTINCT [Date] as item FROM [vSpecial_Billing] WHERE [Collector Name] = ?"; }
if ($_POST['filter'] == 'Date') { $sql = "SELECT DISTINCT [Collector Name] as item FROM [vSpecial_Billing] WHERE [Date] = ?"; }
$stmt = $db->prepare($sql);
$stmt->execute(array($_POST['by']));
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { $results[] = $row; }
}
echo json_encode( $results );
exit;
}
This bit of code is actually pretty straightforward. All it does is determine which filter operation to do, prepares the sql, and then grabs distinct matching rows for output. The key thing though is it outputs as json, so the javascript that called this can handle the data easier!
Now... I had built all this in a test script, and my server hates "fetchAll", so your milage may vary on some of the DB code. I also left out all other form code and db setup handlers and all that. Figuring you have a handle on that.
I hope this helps you out, in some way or other.
EDIT 11/7
I made a slight change because I didn't realize the Collector Names in your db would have characters that would break all of this, oops. Two changes for odd character handling:
The select for collector has its option values wrapped in htmlspecialchars().
The jquery portion for where each select .change event filters, is now filtering by looking for a matching index, using the row.item as a direct variable. Before, it was using it in a value=' row.item ' match, which if the row.item had single quotes (or other bad chars), it would break the whole js event and fail!
Generally when I setup things like this, I use ID's and unique element id tags. That way I am only ever referencing by numbers, and wont run into odd character mash. An example of switching everything to ID's would be involved, and I think you have the gist of whats going on now.

PHP doesn't output error from mysql query for non-existent rows for autocomplete

I have a form that currently is able to auto complete base on user input, it queries the MySQL database and successfully lists all possible matches in the table and give suggestions. Now I want to handle rows that do not exist. I am having trouble to get my PHP file to echo the error. Here is what I have so far:
I'm guessing in my auto search function in my javascript in main.php I need to return the error message to the page?
search.php
<?php
//database configuration
$host = 'user';
$username = 'user';
$password = 'pwd';
$name = 'name';
//connect with the database
$dbConnection = new mysqli($host,$username,$password,$name);
if(isset($_GET['term'])){
//get search term
$searchTerm = '%'.$_GET['term'].'%';
//get matched data from skills table
if($query = $dbConnection->prepare("SELECT * FROM nametbl WHERE name LIKE ? ORDER BY name ASC")) {
$query->bind_param("s", $searchTerm);
$query->execute();
$result = $query->get_result();
//$row_cnt = $result->num_rows;
//echo $row_cnt;
if($result -> num_rows){
while ($row = $result->fetch_assoc()) {
$data[] = $row['name'];
}
//return json data
echo json_encode($data);
mysqli_close($dbConnection);
}
else { echo '<pre>' . "there are no rows." . '</pre>'; }
}
else {
echo '<pre>' . "something went wrong when trying to connect to the database." . '</pre>';
}
}
?>
main.php
<div id="gatewayInput">
<form method="post">
<input type="text" id="name" name="name" placeholder="Name..."><br><br>
<?php
include("search.php");
?>
</div>
...
...
...
<script src ="../../../jqueryDir/jquery-3.2.1.min.js"></script>
<script src ="../../../jqueryDir/jquery-ui.min.js"></script>
<script type="text/javascript">
//auto search function
$(function() {
$( "#name" ).autocomplete({
source: 'search.php'
});
});
1.your method type is post in the form
in main.php
and in the search.php, you have used "if(isset($_GET['term'])){"
this needs to be fixed I guess. either both needs to be POST or GET.
Again you are using include method which the whole code in search.php will be made in-line and treated as a one file main.php. so you need not use GET or Post method.
How does get and Post methods work is
3.1) you have a html or PHP which submits the data from browser(main.php), and this request is being served by an action class(search.php)
example :- in main.php
3.2) now in search.php you can use something like if(isset($_POST['term'])){
You can use num_rows (e.g. if ($result -> num_rows)) to see if the query returned anything.

PHP/MySQL - checkboxes of data from db, submission of 2 parametres

So, i'm struggling with that for a long time.
I need to make a list with checkboxes from DB like [checkbox of car id and value][brand][model]. Then, we need to choose two cars from list, and two cars are chosen - we should block all unchecked till checked won't be unchecked. Then, we need to sumbit into list.php page two car id of chosen cars. And submission won't be done if one or no car are chosen.
I don get it how to realise. Anybody knows how to solve this?
And yes: jquery is desirable, ajax is not ('cos i never worked with that and i don't think i need it here because i need just to transmit parametres to another page)
My code
<?php
$hostname = "localhost";
$username = "root";
$password = "";
$dbname = "jdmdb";
$con = mysqli_connect($hostname, $username, $password) or die(mysqli_error());
$con->query("SET NAMES cp1251");
$con->select_db($dbname);
$result = mysqli_query($con, 'select * from cars') or die(mysqli_error($con));
$rows = $result->fetch_all(MYSQLI_ASSOC);
?>
<html>
<head>
<meta http-equiv="Content-Tуpe" сontent="tехt/html; charset=utf-8">
<title>JDM Database</title>
<script src=../js/jquery-2.1.4.js type=text/javascript></script>
</head>
<body>
<form name=carlist method=post action="list.php">
<?
foreach ($rows as $row) {
echo '<input type=checkbox name="car_short" value="'.$row['car_short'].' id='.$row['car_short'].'"> '.$row['brand'].' '.$row['model'].' '.$row['spec'].' ('.$row['year'].')<br/>';
}
$res = count($rows);
echo '<input type=submit value=123>';
?>
</form>
<?
mysqli_close($con);
?>
</body>
</html>
You can send array of data to list.php
For that you should use following syntax
echo '<input type=checkbox name="car_short[]" value="'.$row['car_short'].' id='.$row['car_short'].'"> '.$row['brand'].' '.$row['model'].' '.$row['spec'].' ('.$row['year'].')<br/>';
This will send send $_POST['car_short'] as array of two selected values. And if you want to check number of selected cars after submission then use
if(count($_POST['car_short'])<2){// place error;}
Else you can use jquery to check how many checkboxes are selected else return false. For that change the form code like this
<form method=post action="list.php" onSubmit="check();">
and check function will be
function check(){
var n = $("input:checkbox:checked").length;
return n>1;
}
Note: Please note the method the exact code may not work properly since i misspelled variables

Second dependent dropdown isn't getting filled with data

I'm trying to make a grade distributions website, and I'm creating 4 dropdowns correlating subject (cs, math, etc.), class (data structures, AI, etc.), professor, and quarter the class was taken. After the quarter dropdown is selected, I want to display a bar graph with the data.
The problem I'm running into is that I can't populate the second dropdown with data Basically, I can successfully pull data from the database for the first dropdown, and if the user selects something then the second dropdown (that was originally hidden using jquery) becomes visible, but it isn't properly pulling data from the database and adding it as options to the second dropdown. An example would be that I can select Computer Science from the first dropdown, then the second dropdown is visible, but it doesn't contain 'intro to programming', 'data structures', etc. in it; instead, it's just blank.
FYI, I'm using these selectpickers: http://silviomoreto.github.io/bootstrap-select/
PHP (error is most likely somewhere in the getClasses function, quite possibly the $_POST section of the code):
<?php
function getSubjects()
{
/* Get mysql connect information from external file and connect*/
require_once 'database.php';
$connection = new mysqli($db_hostname, $db_username, $db_password, $db_database);
if($connection->connect_error) die ($connection->connect_error);
/* Get the column containing the subjects from the table */
$query = 'SELECT DISTINCT Subject FROM gradelist ORDER BY Subject';
$result = $connection->query($query);
if(!$result) die ($connection_error);
/* Keep track of the number of rows in the column; necessary for iterating */
$rows = $result->num_rows;
/* selectBar keeps track of the html code for the select Bar*/
$selectBar = '';
for($j = 0; $j < $rows; $j++)
{
$result->data_seek($j);
$value = $result->fetch_assoc()['Subject'];
$selectBar .= '<option>' . $value .'</option>';
}
$result->close();
$connection->close();
return $selectBar;
}
function getClasses()
{
$connection = new mysqli($db_hostname, $db_username, $db_password, $db_database);
if($connection->connect_error) die ($connection->connect_error);
if(isset($_POST['subject']))
{
$query = "SELECT DISTINCT Class FROM gradelist WHERE Subject = $subject";
$result = $connection->query($query);
if(!$result) die ($connection_error);
}
else
{
die($connection_error);
}
$rows = $result->num_rows;
for($j = 0; $j < $rows; $j++)
{
$result->data_seek($j);
$value = $result->fetch_assoc()['Class'];
$selectBar .= '<option value = "' . $value . '">' . $value .'</option>';
}
$result->close();
$connection->close();
return $selectBar;
} ?>
HTML Portion of the code (again, the error might be with the $_POST part of the code) :
<form class="form-horizontal" method = "post" role="form">
<div class="form-group">
<div class="col-lg-10">
<select name = "subject" id="subject" class="selectpicker show-tick form-control" data-live-search="true" title ="Subject">
<?php echo getSubjects(); ?>
</select>
</div>
</div>
</form>
<form class="form-horizontal" method = "get" role="form">
<div class="form-group">
<div class="col-lg-10">
<select name = "class" id="class" class="selectpicker show-tick form-control" data-live-search="true" title ="Class">
<?php if(isset($_POST['subject'])) echo getClasses(); ?>
</select>
</div>
</div>
</form>
jQuery:
$(document).ready(function() {
$('#class').selectpicker('hide');
$('#professor').selectpicker('hide');
$('#quarter').selectpicker('hide');
});
$('#subject').on('change', function(){
$('#class').selectpicker('refresh');
$('#class').selectpicker('show');
});
$('#class').on('change', function(){
$('#professor').selectpicker('show');
});
$('#professor').on('change', function(){
$('#quarter').selectpicker('show');
});
$('#quarter').on('change', function(){
showTable();
temp = $('#class').selectpicker('val') + " with " + $('#professor').selectpicker('val') + " during " + $('#quarter').selectpicker('val');
$('#displayName').text(temp);
});
Your PHP is executed with $_POST["subject"] not set, and you never POST the subject the user chose to the page; if you don't make an additional POST request, there's no way for the classes to populate.
One way to do it (without changing any of your files) is like so:
$('#subject').on('change', function(){
$.post({
data: { subject: $(this).val() },
success: function (data) {
var classes = $(data).find("#class");
$("#class").replaceWith(classes);
}
});
});
So when a change event is triggered on the subject selection, we'll POST the selected subject to the current page. The response should be the entire document generated with the class selection filled (since $_POST["subject"] is set).
We then replace the current page's #class select element with the version in the generated data (wrapped in $() to create DOM elements from the stringified HTML, so we can use find()).
Another way might be to have files, getSubjects.php, getClasses.php, and so on, and POST individually to them (you make the first request onload, and subsequent requests onchange). This way, you can just append the generated option elements to the select elements on the page.
ALSO: Please please please sanitize $_POST["subject"] before using it in a database query. A user could easily add a fake option to the select locally with a malicious string for value, and you'd unknowingly query the DB with that. You can use prepared statements for this (mysqli has the prepare() function to prepare a statement before querying). More on that and combating SQL injection here.

Categories