Taging and conditional autocomplete - javascript

I'm using http://aehlke.github.io/tag-it/ for taging and autocomplete. As of right now, I can autocomplete for a single term:
Console:
GET .../source.php?term=value01
Javascript:
$("#input-newsearch-2").tagit({
singleField: true,
singleFieldDelimiter: ",",
allowSpaces: true,
autocomplete: ({
source: function( request, response ) {
//var tagterm = $('#input-newsearch-2').val();
$.ajax({
url: "source.php",
dataType: "json",
data: {
//term: tagterm
//term: $('#input-newsearch-2').val()
term: request.term
},
//data: JSON.stringify({ term: $('#input-newsearch-2').val() }),
success: function( data ) {
response( data );
}
});
console.log( request.term );
},
minLength: 3,
select: function(event,ui){
}
})
});
... when hitting key enter the value gets tagged ( alert($('#input-newsearch-2').val() = value01) ) and I can search for the next term.
But when entering the next term, autocomplete searches the whole database each time again. I would like to filter the database considering the previous tags (AND WHERE)? How is this possible?
This is my html:
<input id="input-newsearch-2" autocomplete="off" type="text">
// created by tagit
<ul class="tagit ui-widget ui-widget-content ui-corner-all">
<li class="tagit-choice ui-widget-content ui-state-default ui-corner-all tagit-choice-editable">
<li class="tagit-new">
<input class="ui-widget-content ui-autocomplete-input" autocomplete="off" type="text">
</li>
</ul>
... when I've tagged multiple values #input-newsearch-2 looks like:
$('#input-newsearch-2').val() = value01,value02,value03

Do you mean, you want to concatenate the new entered value to the previous entered value(s)?
You could concatenate the new entry to a globally declared variable.
// before/outside of tagit()...
var terms="";
If the global variable has a length>0, then add a comma then the new entry, otherwise don't add the comma.
// concatenate when you wish:
if(terms.length>0){
terms+=',';
}
$terms+=$('#input-newsearch-2').val();
Then just add the global variable to ajax's data object, and the csv string should be delivered.
data: {
term: terms
},
Alternatively, you could concatenate new entries to an <input name="termstorage" type="hidden" value=""> element, and just have your ajax's collect that value.
I suppose in either instance, you'd also want to offer the ability to clear previous entries from memory.
Here is the php query build part...
I am making the assumption from your other recent/associated question that you are filtering against db columns: name & code
Also, any values entered by the user containing a comma will impact the query. Always, always sanitize user input.
This code uses a loop purely to demonstrate how three different inputs will generate different queries. The loop will not be necessary in your actual case since your input is a single string.
Code: (Demo)
$mock_GET_term[]="";
$mock_GET_term[]="value01";
$mock_GET_term[]="value01,value02,value03";
foreach($mock_GET_term as $csv){
$query="SELECT * FROM `accounts`";
if(strlen($csv)>0){
$where_cond=str_replace(',',"%' OR `name` LIKE '%",$csv);
$query.=" WHERE (`name` LIKE '%{$where_cond}%'";
$query.=" OR `code` LIKE '%{$where_cond}%')";
}
echo "Query = $query\n\n";
}
Output:
Query = SELECT * FROM `accounts`
Query = SELECT * FROM `accounts` WHERE (`name` LIKE '%value01%' OR `code` LIKE '%value01%')
Query = SELECT * FROM `accounts` WHERE (`name` LIKE '%value01%' OR `name` LIKE '%value02%' OR `name` LIKE '%value03%' OR `code` LIKE '%value01%' OR `name` LIKE '%value02%' OR `name` LIKE '%value03%')

Related

fetch db mysql value in search input field and modifying in dropdown

I cannot find a solution for my problem because for me it is an advanced level of programming.
I have a custom search field, but I need to 'convert it' in a dropdown menu that fetching some users values in mysql, avoiding writing hundreds of selection options.
This is the registration form field Im working it
<tr class="user-luogo-wrap">
<th><label for="luogo">
Luogo: </label></th>
<td><input type="text" name="luogo" id="luogo" value="Treviso" class="regular-text"></td>
</tr>
Created with a function
function my_user_contactmethods( $user_contactmethods ){
$user_contactmethods['luogo'] = 'Luogo:';
return $user_contactmethods;
}
add_filter('user_contactmethods', 'my_user_contactmethods', 5);
and this is the field where I need to fetch the 'luogo' mysql values and modifying it in dropdown
<div class="um-search-filter um-text-filter-type "> <input type="text" autocomplete="off" id="luogo" name="luogo" placeholder="Luogo" value="" class="um-form-field" aria-label="Luogo"></div>
I hope I have explained it well. Can someone help me?
In Console I have found the Form data , Request payload generated after a search action:
directory_id=3f9fc&page=1&search=&sorting=display_name&gmt_offset=8&post_refferer=61&nonce=f47827a450&luogo=boston&action=um_get_members
So Im trying to modify this function
function field_choices( $field ) {
// reset choices
$field['luogo'] = array();
// get the textarea value from options page without any formatting
$choices = get_field('my_select_values', 'option', false);
// explode the value so that each line is a new array piece
$choices = explode("\n", $choices);
// remove any unwanted white space
$choices = array_map('trim', $choices);
// loop through array and add to field 'choices'
if( is_array($choices) ) {
foreach( $choices as $choice ) {
$field['luogo'][ $choice ] = $choice;
}
}
// return the field
return $field;
}
add_action('um_get_members', 'field_choices');
Is my intuition correct?

Issues with search bar filter, using JS/JQuery in laravel blade template

I have a blade template with a search bar, which has no submit button and is used for filtering. However, I can't seem to get it to filter appropriately, as the page was originally using angular (which has been removed completely).
My page displays all of my products using foreach loops and displays the info from variables in my page controller (pulling everything from the database and storing as variables). Anyway, everything displays fine but I need help getting this to filter properly.
Basically, if a term entered in the search bar is anywhere in the JSON object gathered by the controller, then I want it to only display those objects. I may even need another foreach loop.
Here's the html/blade code:
<!--Search bar div-->
<div class="uk-width-5-10">
<div class="md-input-wrapper search-form">
<form id="searchProducts">
<input type="text" class="md-input label-fixed" name="srch-term" id="srch-term" autofocus placeholder="Search Products"/>
<span class="md-input-bar"></span>
</form>
</div>
<!--foreach loops around the wrapper that shows products, for reference-->
#foreach ($orderFormData->pgroups as $pgroup)
#foreach ($pgroup->image_names as $image_name)
#foreach ($pgroup->pskus as $psku)
Javascript for the search (see the variable for the JSON object, that's what I need to search within)
<script>
var orderFormData = <?php echo json_encode ($tempdata);?>;
</script>
<script>
var orderData = orderFormData // default value
var search = function (e) {
var term = e.currentTarget.value
orderData = Object.entries(orderFormData).reduce(function (data, entry) {
if (entry[0].match(term) || entry[1].match(term)) {
data[entry[0]] = entry[1]
}
return data
}, {})
console.log(orderData)
}
document.querySelector('#srch-term').addEventListener('keyup', search)
</script>
Is there a better way I should be doing this? I may even need to do a foreach loop around the search bar
It kind of sounds like you're looking for an auto complete. Have you looked at the jquery-ui-autocomplete library? It's pretty easy to implement, and might add more functionality more easily than writing loops yourself.
https://jqueryui.com/autocomplete/
I'll get into why I named the function below, but here's my implementation:
monkeyPatchAutocomplete();
$("#your_searchbox_selector").autocomplete({
source: // http://Your_Search_URL_endpoint_here,
delay: 500, // prevents search from running on *every* keystroke
minLength: 1, // default is 2, change or remove as you like
// open page after selecting (with enter key).
select: function( event, ui )
{
var qval = ui.item.id // this pulls whatever field you're looking for in your JSON that you want to use to direct your user to the new page, in my case "id";
var url = 'http://whereever_you_want_your_user_to_go?';
window.location = url + qval;
}
});
For my implementation, I wanted to color code the results in my autocomplete list with active and inactive entries, so my search controller JSON result includes 3 fields:
'value' => $searchable_values, 'id' => $id_mapping_of_whatever, 'class' => $css_classes_to_use
My search controller plugs in emails, names, and phone numbers to the value field, which is searchable, then maps an id, and plugs in css classes that I use to change the text color of the results through a monkeypatch on jQuery's autocomplete:
function monkeyPatchAutocomplete()
{
$.ui.autocomplete.prototype._renderItem = function( ul, item)
{
var re = new RegExp(this.term, 'i');
var t = item.label.replace(re,"<span class='autocomplete-span'>" + this.term + "</span>");
return $( "<li></li>" )
.data( "item.autocomplete", item )
.append( "<a class='text-" + item.class + "'>" + t + "</a>" )
.appendTo( ul )
};
};
If you're interested in formatting your results, check out dev.e.loper's answer to: How can I custom-format the Autocomplete plug-in results?.

Is there a way to show datalist options that don't match user input?

I've got two elements, a datalist and an input:
<datalist id="choices"></datalist>
<input id="custom_id" name="custom_name" type="text" value="" list="choices" class="medium-field"/>
Additionally,here is an AJAX request, which populates the datalist (this works; not my issue)
var params = {
"partial": $("#custom_id").value;
};
$.ajax({
cache: true,
url: "http://www.example.com",
type: "POST",
data: JSON.stringify(params), // contains the input.value
contentType: "application/json",
headers: {
"Range": "0-9"
},
dataType: "json",
processData: false,
// On success, create <option> elements and append them to datalist
success: function(data) { .. };
The data returned by the REST endpoint is formatted like so:
[
{
"label": "active",
"name": "iron"
},
..
]
Basically the AJAX request hits a REST endpoint in front of a PostgreSQL db that does runs a SELECT based on the provided partial parameter, and returns a 2-column table (which gets formatted as the above response).
However, I have situations where the data returned from the AJAX request is spelled differently from the user's input; for example, when input.value = 'magnet', I sometimes return a list of options where a few might read 'iron'.
The problem : due to 'iron' being spelled differently than 'magnet', the user won't see this option in the datalist dropdown (even though the option element is created) unless the user actually types 'iron'. Is there a way for me to display 'iron' in the datalist even though it doesn't match what the user has typed?
As far as I can understand your question, you are willing to show the synonyms as a result when an input is typed in the search box, i.e if a user types magnet, he shall see the synonyms to 'magnet', which in this case happens to be iron
What you can do is when the ajax hit is sent to example.com I understand that there is some SQL code as SELECT keyword FROM table_name WHERE keyword LIKE '%<whatever string is in data>%'. This would return 'magnet' even if the user types mag. Now coming to returning 'iron' and 'magnet' when a user types say 'mag'
You will have to modify the table a little bit as follows:
1. Create a keywords table having columns: id, keyword and synonym_to_keyword
2. Enter the data like
(1, 'iron' , null)
(2,'magnet',1)
Now modify the query as: SELECT id as I, keyword FROM table_name WHERE keyword like '%<whatever string is in data>%' OR keyword like (SELECT keyword FROM table_name WHERE synonym_to_keyword = I)
Note that you will have to maintain a list of keywords and probable synonyms to the keywords so that the user sees all the probable options (like magnet, iron, etc) when he types 'magnet'.
Since Chrome will compare both the value and label to see if it should show the datalist option, you can set all of your option labels to the value you have in your input for them to show up.
Relevant Chromium patch
Code Example below:
function autoSuggestAddress(input) {
if (input.value) {
// dummy data
let suggestions = ["Example 1", "Example 2", "Example 3"];
// clear old datalist
let datalist = input.list;
while (datalist.hasChildNodes()) {
datalist.removeChild(datalist.firstChild);
}
suggestions.forEach((item) => {
let option = document.createElement("option");
option.label = input.value;
option.value = item;
datalist.appendChild(option);
});
}
}
<form method="GET" id="search-form">
<label for="address">Address</label>
<input type="text" id="address" name="address" form="search-form" oninput="autoSuggestAddress(this)" type="search" value="" required autocomplete="off" list="suggestions" />
<datalist id="suggestions">
</datalist>
</form>

passing primary key instead of attribute on submit

I have an input tag that takes a users input that calls an AJAX dynamically outputs suggestions from my database. The issue is I want to store the primary key associated with that attribute.
I have figured out a way set it to the primary key when the user selects a value; however I would rather only have the attribute displayed on the front end. Essentially what I was thinking about doing was using the option tag and setting the value to the primary key, but after reading the documentation for it, that doesnt look like it would work.
HTML:
<input type="text" id = "zip_id" class="tftextinput2" autocomplete = "off" name="zip" placeholder="Zip Code" onkeyup = "autocompleter()">
<ul id = "zip_codes_list_id"></ul>
JS:
function autocompleter()
{
var min_length = 1; // min caracters to display the autocomplete
var keyword = $('#zip_id').val();
if (keyword.length >= min_length) {
$.ajax({
url: 'ajax_refresh.php',
type: 'POST',
data: {keyword:keyword},
success:function(data){
$('#zip_codes_list_id').show();
$('#zip_codes_list_id').html(data);
}
});
} else {
$('#zip_codes_list_id').hide();
}
}
// set_item : this function will be executed when we select an item
function set_item(item)
{
// change input value
$('#zip_id').val(item);
// hide proposition list
$('#zip_codes_list_id').hide();
}
PHP:
<?php
//connect to db here
$keyword = '%'.$_POST['keyword'].'%';
$sql = "SELECT * FROM zip_codes WHERE zip LIKE (:keyword) ORDER BY zip_codes_id ASC LIMIT 0, 10";
$query = $pdo->prepare($sql);
$query->bindParam(':keyword', $keyword, PDO::PARAM_STR);
$query->execute();
$list = $query->fetchAll();
foreach ($list as $rs)
{
// put in bold the written text
$zip = str_replace($_POST['keyword'], '<b>'.$_POST['keyword'].'</b>', $rs['zip']);
// add new option
// echo '<li onclick="set_item(\''.str_replace("'", "\'", $rs['zip']).'\')">'.$zip.'</li>'; (this one only passes the attribute)
echo '<li " onclick="set_item(\''.str_replace("'", "\'", $rs['zip_codes_id']).'\')">'.$zip.'</li>';
//this one passes the attribute but changes the displayed value to the primary key.
}
?>
As you can see from the PHP file, what I am trying to do is pass in the primary key value but keep the displayed value the attribute. I am not sure how to do that. Should I be using the UL tag?
The issue in your code is that you try to the zip_id value for the input, but this input contains the zip field value - I assume it's the textual representation. There are a few ways how you could save the zip_id on the frontend - either store it in the model (if you're using some MVC framework, but I gues it's not the case) or simply add a hidden input field:
<input type="hidden" id="actual_zip_id" name="zip_id">
And
function set_item(item)
{
// change input value
$('#actual_zip_id').val(item);
// hide proposition list
$('#zip_codes_list_id').hide();
}
UPD
Speakng about the entire idea of autocompleting zip codes, it looks pretty nasty, as pointed by Darren Gourley (check the comments).
So you'd rather validate it with regex first, and then do your db-related logic like that:
$('#zip_id').on('change', function(){
// your stuff
})
Best regards, Alexander

Selecting an autocomplete list does not save the word fully when using ajax to save

I built an autocomplete list of cities using a mysql query. Typing in the input field correctly initiates the search and builds the selectable city list. The problem is that the city name in full does not get saved, only the characters typed when using .on('change'... I tried other mouse events such as mouseover, mousemove, mouseout, mouseenter, mouseleave - and they require the user to first select the item from the list, move out, then swipe the cursor over the input a second time to actually trigger the save of the selected city name. The html looks like:
<input type="text" onmouseover="this.focus();" class="saveCity" name="pob_city" member_id="<?php echo $member_id; ?>" value="<?php echo $pob_city; ?>" placeholder="City" id="pob_city" />
<script language="javascript" type="text/javascript"> $("#pob_city").coolautosuggest({ url:"tools/autosuggest/query_cities.php?chars=" }); </script>
The saveCityCountry javascript:
$(document).on('mouseout','.saveCity',function()
{
var DATA=$(this).val();
var n =remove_whitespaces(DATA);
var MEMBER_ID=$(this).attr('member_id');
$("#Results").html( "" );
if(n !=''){
var dataString = 'pob_city='+ n +'&member_id='+MEMBER_ID;
$.ajax({
type: "POST",
url: "saveData.php",
data: dataString,
cache: false,
success: function(html)
{
$("#Results").html( html );
}
});
}
})
You can see a demo at: http://dottedi.us/misc/ajaxsave/
The other thing that is slightly bothersome is that with ordinary input fields such as first or last name, the on change event only triggers when you click somewhere else on the page, thus the placebo "Save" button.
Update: I cleaned up the code in my example splitting off City from Country and updated the post to reference saveCity. This will not affect the result, just a bit easier to follow.
Inside the http://dottedi.us/misc/ajaxsave/tools/autosuggest/js/ folder are two javascript files that might be relevant, jquery.coolautosuggest.js and jquery.coolfieldset.js. On line 46 of jquery.coolautosuggest.js I see success:function(data){. You want me to place "source: data, autoFocus: true, delay: 0, minLength: 0, select: response, response: response" somewhere in here? Please make it clear where and with the right syntax, semicolin, etc.
In the codeblock you added, I assume that I change the HTML adding the ,response and the function response just gets placed somewhere within my saveData.js. Once this is right, do I go back to change as opposed to mouseout or mouseover?
include response function in your autocomplete call, where you can assign the selected value to hidden field, This will allow you to retain full text.
I assume you are getting array of cities, store each city in an array in a key called "city" and return it.
Define one hidden field say "selected_city".
Inside your coolautosuggest success function place this code
source: data,
autoFocus: true,
delay: 0,
minLength: 0,
select: response,
response: response
$("#pob_city").coolautosuggest({ url:"tools/autosuggest/query_cities.php?chars=" },response);
function response(){
if (typeof ui.content != "undefined" && ui.content.length === 1) {
city= ui.content[0].city;
}
else if (typeof ui.item != "undefined" && ui.item.label.length > 0) {
city= ui.item.label;
}
if (label.length > 0 || index.length > 0) {
$("#selected_city").val(city);
// this value you can use to display
}
}
before doing this please change the below function on jquery.coolautosuggest.js at line 120
if(me.submitOnSelect==true){ $("form").has(me.textField).submit();
}
to
if(me.submitOnSelect==true){
$("#pob_city").val($(this).find(".suggestion_title").text()); $("form").has(me.textField).submit();
}

Categories

Resources