Ajax/jQuery live search is duplicating the output results - javascript

I'm currently working on Ajax and jQuery live search which finds a results in a JSON file. Script is working fine, but the is only one problem - it's duplicating the result data.
EXAMPLE:
MARKUP:
<div class="row">
<h3>Live Search Results</h3>
<div id="update-results">
<p>event_name | club_name | memberid</p>
<ul id="update">
<!-- <li></li> -->
</ul>
</div>
</div>
SCRIPT:
$('#search').keyup(function() {
var searchField = $('#search').val();
var $update = $('#update');
$update.empty();
$.get("getEventsWithVideos.php?text=" + searchField, function(data) {
var vals = jQuery.parseJSON(data);
if($.isArray(vals['Event'])) {
$.each(vals['Event'], function(k,v){
$update.append("<li value='"+v['id']+"'><a href='#'>" + v['event_name'] + "</a></li>");
});
} else {
$update.append("<li value='"+vals['Event']['id']+"'><a href='#'>" + vals['Event']['event_name'] + "</a></li>");
}
});
});
I've tried to debug and stop the error, but it was unsuccessful. Can anyone help me please with that?

Put the empty() inside the response handler:
$.get("getEventsWithVideos.php?text=" + searchField, function(data) {
$update.empty();
basically you are clearing the list on every keystroke (rapid), then requesting the data, then (sometime later) appending the results that come back (which could be multiple results depending on the timing).

I didn't reproduce your error but I suspect that you have problem with multiple request to server and adding them all instead of last one. Probably adding below code will fix your problem
$update.empty();
Anyway I suggest you to use 2 more functions: throtlle and debounce from underscore to prevent too much request on every keyup.
Also you could try Rx.js witch give following example (https://github.com/Reactive-Extensions/RxJS):
var $input = $('#input'),
$results = $('#results');
/* Only get the value from each key up */
var keyups = Rx.Observable.fromEvent($input, 'keyup')
.map(function (e) {
return e.target.value;
})
.filter(function (text) {
return text.length > 2;
});
/* Now debounce the input for 500ms */
var debounced = keyups
.debounce(500 /* ms */);
/* Now get only distinct values, so we eliminate the arrows and other control characters */
var distinct = debounced
.distinctUntilChanged();

Try changing this line $update.empty(); of your code to $update.find('li').remove(); and put it inside the response handler.
This removes all the previous data before you append the new values. Hopefully it might work.

Related

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?.

Display results from api after user input

I'm learning JS and I need some help figuring out why my info isn't getting populated in the html. I'm just trying to get the basic functionality to work, so that I can continue to expand on it.
User is supposed to input a 3 digit route value, which will then return all the route information from an api call. I was able to get the route info to display earlier when I got the api call set up, but I'm struggling to figure why it's not displaying now that I tried adding in a feature to allow the user to input the route. See attached pen
HTML
<div class='container'>
<h1 id='header'>Route Info</h1>
<input id="input" type="text" placeholder="Enter 3 digit route ex 005" >
<input type="button" value="Get Route" onclick="getRoute()">
<br>
<p id = 'p'><span id="routeInfo"></span></p>
</div>
Javascript
$(document).ready(function() {
var route = $('#input');
getRoute.click(function() {
var scriptTag = document.createElement('SCRIPT');
scriptTag.src = "https://wsdot.wa.gov/Traffic/api/Bridges/ClearanceREST.svc/GetClearancesAsJson?AccessCode=59a077ad-7ee3-49f8-9966-95a788d7052f&callback=myCallback&Route=" + route;
document.getElementsByTagName('HEAD')[0].appendChild(scriptTag);
var myCallback = function(data) {
var myarray = Array.prototype.slice.call(data);
document.getElementById("routeInfo").innerHTML = JSON.stringify(myarray);
}
});
});
It looks like you are jumping through a lot of hoops you don't need to. As long as you are using Jquery, you should look into getting the api data with an ajax request. It's much easier and more intuitive. Also you have a few problems such as trying to get the input value with var route = $('#input'); which return the actual input element. You are also processing the returned data in a way that won't work.
Here's a basic example to get you going on (IMO) a better track:
function getRoute() {
var route = $('#input').val();
var url = "https://wsdot.wa.gov/Traffic/api/Bridges/ClearanceREST.svc/GetClearancesAsJson?AccessCode=59a077ad-7ee3-49f8-9966-95a788d7052f&Route=" + route;
$.ajax({url: url, success: function(data){
var retValue = "";
var i = 0
for(i; i< data.length; i++) {
retValue += data[i].BridgeName + "<br>"
}
document.getElementById("routeInfo").innerHTML = retValue;
}});
}
If you intend functionality in the getRoute.click callback to run, you need to rewrite that as a method function getRoute(), or get the button element via jQuery and assign that to the variable getRoute. As it stands, you have the click method wired via the markup to a function named getRoute which does not exist. In the JS you are trying to register a click event to a jQuery object named getRoute which does not exist.
getRoute needs to be a global function for it to be called from html :
getRoute = (function() {
Also, myCallback needs to be a global function for it to be called from your loaded script (just remove the var):
myCallback = function(data) {

Having difficulty building a form summary with JS

Sorry for the noobish question but, I am trying to build a form summary that will populate a div (immediately) with all of the fields being used. Here is a small sample of the field: Fiddle
For some reason the JS is not working as I would expect it to, can anyone point out what I am doing wrong?
For example, I would like it to output: "AND name: john EXCEPT number 222".
I would also like to be able click on a result to remove it, and clear the field. Thank you
$(".allS").change(function () {
if ($(this).next('.textArea').not(':empty'))
// varible to hold string
var str = "";
$("select option:selected").each(function () {
str += $(this).text() + " ";
});
$("#text_here").text(str);
}).change();
$('.textArea').change(function(){
var $inputs = $('form#form :input[type="text"]'),
result = "";
$inputs.each(function(){
// access the individual input as jQuery object via $(this)
result += $(this).val()+"<br>";
});
// store result in some div
$('div#text_here').text(result);
}).change();
There were many mistakes in your code. I simplified it to a very short code that only does what's needed to get the output you requested. Here's the working fiddle.
$(".allS, .textArea").change(function () {
var str = '';
if ($('#name').val().length > 0 && $('#number').val().length > 0)
var str = $('#nameMod>option:selected').text() + ' name:' + $('#name').val() + ' ' + $('#numberMod>option:selected').text() + ' number ' + $('#number').val();
$("#text_here").html(str);
});
Basically, what this does is attach a change event handler to both classes (.alls, .textArea), and when the event is triggered, both input fields are tested for any content. If this test passes, a string is composed out of all the relevant values, and the div content is set. If the test failed (no content), the str variable contains an empty string and the div is cleared.
Just glancing at the code, the selector 'form#form :input[type="text"]' looks wrong. For starters, input is not a pseudoclass. Also, attribute matching shouldn't have the quotes.
This may or may not be what you want (I think it is, from looking at your html):
'form#form input[type=text]'
Also your <br>'s are not working because you called text(). call html() instead.

Jqgrid inline mode with select2

I have found the #Olegs answer for FORM based select2 integration to jQgid, but I need help to get it to work in inline mode,, this jsfiddle is my attempt to get my problem online somehow I'm new with fiddle so please be patient :)
http://jsfiddle.net/mkdizajn/Qaa7L/58/
function(){ ... } // empty fn, take a look on jsfiddle
On this fiddle I can't make it to work to simulate the issue I have in my local network but the problem with this select2 component is that when I update some record(via local or ajax), the grid does not pick up my change and it sends null for values where select2 fields are!
I'm sorry that I can't make jsfiddle to work like on my PC :(
Thanks for any help you can think off that may be the issue here..
P.S. one veeeery strange thing is that when I console.log( select2-fields ), before, and after the value is picked up correctly but I suspect that the grid loose that value somewhere in between .. and send null values to server..
I'm posting this in a good will that I think will help anyone if come to close incounter with similar problem like me..
I'll try to bullet this problem out step by step..
first, on my server side I generate one html tag somewhere near grid table that holds info what columns, fields are lookup type.. like this:
<div id="hold_lookup_<?=$unique_id?>" style="display: none"><?php echo $lokki; ?></div>
that gives me output like this:
<div id="hold_lookup_table1" style="display: none">col1+++col2+++col3</div>
define onselectrow event somewhere
$onSelectRow = "function(){
f = $(this).attr('id'); // grid name
try{
n = $('#hold_lookup_' + f).text().split('+++');
}catch(e){
console.log(e)
}
rez = ''; // results
temp = 'textarea[name='; // template
$.each(n, function(index, item){
rez += temp + item + '],'
});
rez = rez.slice(0,-1); // rezemo zadnji zarez
$( rez ).select2({ .. define my ajax, my init etc.. });
}";
$dg->add_event("jqGridInlineEditRow", $onSelectRow);
last but very tricky part is here.. I destroy select2 columns before sending to database in jqgrid.src file where function for SAVE inline method is.. like this
if (o.save) {
$($t).jqGrid('navButtonAdd', elem, {
caption: o.savetext || '',
title: o.savetitle || 'Save row',
buttonicon: o.saveicon,
position: "first",
id: $t.p.id + "_ilsave",
onClickButton: function() {
var sr = $t.p.savedRow[0].id;
rez = rez.split(',');
rez1 = '';
$.each(rez, function(index, item) {
rez1 += item + ','
})
rez1 = rez1.slice(0, -1);
rez1 = rez1.split(',');
$.each(rez1, function(index, item) {
$(item).select2('destroy');
});
you can see that I inserted the code onclickbutton event via same 'rez' variable that was defined in my php file where I created grid..
That's it, I hope that helped someone, event if not in this particular problem, but with methods that was used here :)
cheers, kreso

ngInit not working asynchronously(with $q promise)

Edit:
Plunker is working, actual code isn't:
http://plnkr.co/edit/5oVWGCVeuTwTARhZDVMl?p=preview
The service is contains typical getter\setter stuff, beside that, it functions fine, so I didn't post it's code to avoid TLDR.
TLDR version: trying to ng-init a value fetched with AJAX into the ngModel of the text-area, the request resolves with the correct value, but the textarea remain empty.
parent controller function(talks to the service):
$scope.model.getRandomStatus = function(){
var deffered = $q.defer();
var cid = authService.getCompanyId();
var suggestions = companyService.getStatusSuggestions(cid);
if(suggestions && suggestions.length > 0){
deffered.resolve(suggestions[Math.floor(Math.random(suggestions.length) + 1)].message);
return deffered.promise;//we already have a status text, great!
}
//no status, we'll have to load the status choices from the API
companyService.loadStatusSuggestions(cid).then(function(data){
companyService.setStatusSuggestions(cid, data.data);
var result = data.data[Math.floor(Math.random(data.data.length) + 1)];
deffered.resolve(result.message);
},
function(data){
_root.inProgress = false;
deffered.resolve('');
//failed to fetch suggestions, will try again the next time the compnay data is reuqired
});
return deffered.promise;
}
child controller:
.controller('shareCtrl', function($scope){
$scope.layout.toggleStatusSuggestion = function(){
$scope.model.getRandomStatus().then(function(data){
console.log(data);//logs out the correct text
//$scope.model.formData.shareStatus = data;//also tried this, no luck
return data.message;
});
$scope.model.formData.shareStatus = $scope.layout.toggleStatusSuggestion();//Newly edited
}
});
HTML:
<div class="shareContainer" data-ng-controller="shareCtrl">
<textarea class="textAreaExtend" name="shareStatus" data-ng-model="model.formData.shareStatus" data-ng-init="model.formData.shareStatus = layout.toggleStatusSuggestion()" cols="4"></textarea>
</div>
I believe what you are wanting is :
$scope.model.getRandomStatus().then(function(data){
$scope.model.formData.shareStatus = data.message;
});
Returning something from within then does not return anything from the function wrapping it and therefore does nothing
Turns out that I had a custom validation directive that was watching the changes in the model via $formatters, and limting it to 80 chars(twitter), it was failing silently as I didn't expect to progmatically insert invalid values into my forms, very stupid, but could happen to anyone.
Had to make some changes to it, so it's worth to remember in case it happens to anyone else.

Categories

Resources