More user friendly search box in MVC - javascript

I have implemented searchbox using jQuery. Here is the code which sends search term and
after that I receive Json which I use to make list of matched searched items.
The problem is that on each keyup I delete all matched items :
$("#realPlaceForSearchItems").html("");
because if I don't that I get duplications when searching for product if I enter "pro" and then type "d". (I am appending list items to the list) Is it possible to achieve that I somehow just delete elements that do not match "prod" (which previously matched "pro" ofcourse) and that elements that match prod stay untouched after typing "d".
$("#searchInput").keyup(function () {
$this = $(this);
$('#realPlaceForSearchItems').show();
$("#realPlaceForSearchItems").html("");
var seachedTerm=$this.val();
if ($this.val().length> 2)
{
$("#realPlaceForSearchItems").html("");
$('#realPlaceForSearchItems').show();
$.ajax({
type: "POST",
url: ROOT + "Filter/Search/",
data: {term: $this.val()},
success: function (data)
{
var Link = $("#searchTemplate>li>a");
var placeForProductId=$("#searchTemplate>li>a>input");
var placeForPicture = $("#searchTemplate>li>a>div>img");
var placeForProductName = $("#searchTemplate>li>a>div>div");
var placeForPrice= $("#searchTemplate>li>a>div>span");
$.each(data.productsWereSeached, function () {
console.log("sddsd", data.totalrows);
var imagesFolder="/Content/images/";
var pathToProduct="/ProductDetails/Index/"
var slash = "/";
Link.attr("href", pathToProduct + this.Id);
placeForProductId.val(this.Id);
if (this && this.Picture) //for the case there is no any picture there would be error cant read propery or undefined
placeForPicture.attr("src", imagesFolder + this.Id + slash + this.Picture.FileName);
else
placeForPicture.attr("src", "");
placeForProductName.html(this.Name);
placeForPrice.html((parseFloat(this.Price) / 100.0).toString() + " kn");
$listItem = $("#searchTemplate").html();
$("#realPlaceForSearchItems").append($listItem);
});
$("#nOfMatchedProducts").val(data.totalrows);
if (data.totalrows > 2)
{
var searchurl="/Search/ShowMoreSearched?term="
$showMoreItem = $("#showMoreItem").html();
$("#showMoreItem>li>a").attr("href",searchurl+seachedTerm);
$("#realPlaceForSearchItems").append($showMoreItem);
}
},
failure: function ()
{
}
});
}
});

$.each(data.productsWereSeached, function () {
if($('a[href="'+pathToProduct + this.Id+'"]').length == 0) {
console.log("sddsd", data.totalrows);
var imagesFolder="/Content/images/";
var pathToProduct="/ProductDetails/Index/"
var slash = "/";
Link.attr("href", pathToProduct + this.Id);
placeForProductId.val(this.Id);
if (this && this.Picture) //for the case there is no any picture there would be error cant read propery or undefined
placeForPicture.attr("src", imagesFolder + this.Id + slash + this.Picture.FileName);
else
placeForPicture.attr("src", "");
placeForProductName.html(this.Name);
placeForPrice.html((parseFloat(this.Price) / 100.0).toString() + " kn");
$listItem = $("#searchTemplate").html();
$("#realPlaceForSearchItems").append($listItem);
}
});

Psst... I'm assuming you also want to limit calls to the server and that your search result list is not wildly huge!
So! Benefit to your current approach is you don't have to manage/compare any existing data set. This makes things easier when a search for "pro" changes to a search for "cro" or any other change that makes the previous AJAX call irrelevant. But, like you said, it leaves you with this clear then re-add items inefficiency when you search for "prod" after "pro".
Idea:
Store the most recent AJAX call criteria in a global.
If new search value includes the latest AJAX search value, filter/hide items in the existing data set which do not match the new criteria. Do not perform a new search.
If new value does not include the latest AJAX search value: clear current data set, update AJAX search value, execute new AJAX call

Pass the index from $.each (http://api.jquery.com/jQuery.each/) into your function, then use it to select the search result you will replaceWith (http://api.jquery.com/replaceWith/) the element you just built. In using this method, your four LI elements within the search results UL must exist before a search.keyup is executed.
Do this by changing two lines...
$.each(data.productsWereSeached, function (index) {
... all of the existing code in the loop stays the same except ...
$("#realPlaceForSearchItems LI:eq(" + index + ")").replaceWith($listItem);
});

Related

Getting multiple hash query string values in an array javascript

The purpose of this code is to open two different tabs where one tab is nested inside main tab. When user click a link which passes a query string hash values like the following
http://127.0.0.1:5500/products/virtual-machines.html#Get_Started#v-pills-02-tab
I need to open both tabs at once when page loads.Bootstrap 5 is the plugin used for. I tried using the following code.
var hash = location.hash.split('?')[0];
console.log(hash);
if (hash) {
var triggerEl = document.querySelector("#" + hash + '');
triggerEl.click();
}
result
#Get_Started#v-pills-02-tab
I need to slipt in two var
Get_Started
v-pills-02-tab
Your example code is splitting on a question mark (?), which doesn't exist in your hash.
Perhaps you could write your hash as a comma separated list, the first item in the list would be the parent tab element id, and the 2nd item would be the child tab element id, etc.
http://127.0.0.1:5500/products/virtual-machines.html#Get_Started,v-pills-02-tab
Then parse it, splitting on the comma.
// substring out the leading '#'
// and convert the comma-separated items into an array
var tabIds = location.hash.substring(1).split(',');
if(tabIds) {
var $tab = $('#' + tabIds[0]);
// Ensure that the element exists before clicking it
if($tab.length > 0) {
$tab.click();
// Check for subsequent tab-ids
if(tabIds.length > 1) {
var $subTab = $('#' + tabIds[1]);
if($subTab.length > 0) {
$subTab.click();
}
}
}
}
(Sorry if you're allergic to jQuery. It shouldn't be difficult to translate it to vanilla JS.)

Ajax call to get list of objects clicked on

I am trying to use an AJAX call to send data to my django view, which I then hope to use in another view. When a user clicks on a particular word, known_words must go up by one (this part works). But I also want to know which word the user clicked on (I have access to this in the template: {{item.0}}. And this is the part that I cannot get to work.
The relevant part of my html (this is the last column of my table, the first column contains {{item.0}}):
Yes
My js:
$(document).ready(function() {
var known_words = 0;
var clicked_words = [];
$(".word_known").click(function() {
known_words++;
var reference = this;
var songpk = $(this).data('songpk');
var userpk = $(this).data('userpk');
var clicked_words = $(this).data('clicked_words'); //I know this part is wrong, how can I append the word to the list?
$.ajax({
url: "/videos/songs/vocab/known/"+songpk+"/"+userpk+"/",
data: {known_words: known_words, clicked_words: clicked_words},
success: function(result) {
$(reference).removeClass("btn-warning");
$(reference).addClass("btn-success");
$(reference).text("Known");
},
failure: function(data) {
alert("There is an error!")
}
})
});
})
Views:
def word_known(request, pk_song, pk_user):
if request.method =='POST':
pass
elif request.method == 'GET':
known_words = request.GET.get('known_words', '')
clicked_words = request.GET.get('clicked_words', '')
request.session['known_words'] = known_words
clicked_words = []
clicked_words.append(request.session['clicked_words'])
print('The number of known words is {} and clicked words are {}'.format(known_words, clicked_words))
return HttpResponse(json.dumps(known_words))
In the console, when I click on a word (not 'hello'), I get the following in the console:
The number of known words is 1 and clicked words are ['hello']
And if I click a second time on a different word:
The number of known words is 2 and clicked words are ['hello']
So the counter is working, but not the word list. How can I fix that?
I haven’t tested this, but I think you are overwriting the Array instead of adding to it.
This line
var clicked_words = $(this).data('clicked_words');
Should be
clicked_words.push($(this).data('clicked_words'));
This is a link to the documentation on MDN
In addition to #Daniel Butler's answer, I had to change my view as follows:
clicked_words = request.GET.getlist('clicked_words[]')
Because apparrently when you send a list through jQuery it changes the keyword as well.

Modifying HTTP GET Request via Select Menu Change - Update Parameter

I have a search results page in a PHP site that returns a list of results using pagination. The URL looks like this:
findProducts.php?action=searchAssets&orderNumber=xxxx&productName=zzz&skip=20
I have a select menu that allows the user to modify/filter the search results which triggers a script like this:
$(document).ready(function() {
$('#productType').change(function() {
window.location.href = window.location.href + '&productType=' + $(this).val();
});
});
This is working well except for one thing - I need to reset the 'skip' parameter to 0 for the new filter search as the pagination values from the previous search won't be valid or applicable. Is there a way I can change:
skip=20
to:
skip=0
as part of this script?
You could do a RegExp replace on the URL:
window.location.href = window.location.href.replace(/((?:\?|&)skip)=\d+/, '$1=0') + '...';
(untested)
Note that you should do the same with the productType because otherwise you'll add it again and again.
Better solution would possibly be to have a base URL and then add all necessary parameters instead of doing search and replace...
You can get the query from the URL by splitting the URL using ?
This will give you the base url in the first index and the query in the second.
You can then get the query parameters by splitting the query using &.
You can loop through all of the parameters checking if it is the skip parameter. If the parameter is the skip parameter push your new value to an output array. Otherwise push the unchanged parameter to an output array.
You can then use join to join all of your output elements using & to reconstruct the query and return your original base url with your new query string.
<script>
function fixQuery(qstr) {
var parts = qstr.split('?');
var query = parts[1];
var a= query.split("&");
var out=[];
for (var i = 0; i < a.length; i++) {
var b = a[i].split('=');
if(decodeURIComponent(b[0])=="skip")
{
out.push("skip=0")
}
else {
out.push(a[i]);
}
}
return parts[0] + '?' + out.join("&");
}
var result= fixQuery("http://example.com/findProducts.php?param1=test+thing&param2=hello&skip=10");
console.log(result)
//http://example.com/findProducts.php??param1=test+thing&param2=hello&skip=0
</script>

Order data in Firebase

I'm new to Firebase, and I'm going to build a list of item collected from server with following criterias:
max 10 items
order by item value (eg. 'count')
this is my code
FBRef.orderByChild("count").limitToLast(10).on("child_added", function(snapshot){}
and it worked fine, but when a new record inserted, is there a Firebase method that can check and auto update the list if the new item is in top 10 ? Or I need to to it myself ?
var FBRef = new Firebase('yours.firebaseio.com');
FBRef.orderByChild("count").limitToLast(5).on("child_added", function(snapshot){
var data = snapshot.val();
var html = "<li>value: " + data.value + " - count: " + data.count + "</li>";
$("#result").prepend(html);
});
$(document).ready(function () {
$("#inputaddbtn").click(add);
});
function add(){
var value = $("#inputvalue").val();
var count = $("#inputcount").val();
$("#inputcount").val("");
$("#inputvalue").val("");
FBRef.push({
value : value,
count : count
})
}
You'll need to monitor more events and handle more arguments to keep the list limited.
As a first step: Firebase will fire a child_removed event when an element has been removed from the location or (in your case) falls outside of the query.
You can handle that event to remove the child from the list:
var FBRef = new Firebase('yours.firebaseio.com');
var query = FBRef.orderByChild("count").limitToLast(5);
query.on("child_added", function(snapshot){
var data = snapshot.val();
var html = "<li id='key_"+snapshot.key()+"'>value: " + data.value + " - count: " + data.count + "</li>";
$("#result").prepend(html);
});
query.on("child_removed", function(snapshot){
$('#key_'+snapshot.key()).remove();
});
You'll note that I give the li an id when the child is added and then remove the li based on that id when Firebase tells me to.
This covers simple adding/removing items. For a complete app that handles all cases, you should also handle child_changed and child_moved. And you'll probably also want to handle the second argument to the callbacks, which indicates the key of the item that the child needs to go after. But maybe your use-case doesn't require handling these cases, in which case the above is enough.

Inserting a string at a variable index

I have not been able to find a working example or a good explanation of how I can achieve the following: (I would appreciate if anyone can point me in the right direction.
I have a query string: **"/api/bla?sources=[1,2]&plans=[1,2]&codes=[1,2,3]"**
I will be updating the query string via either javascript or jquery when certain events occur on my page, doesnt matter which.
For example, there is a multi select dropdown on the page which houses [sources] and [plans] and [codes]... These dropdowns have IDs which i am to update my request url with upons selecting items in teh dropdowns.
When a source with ID "3" is selected from the dropdown (or checkbox, doesnt matter what page controls are being used) the query string parameter sources[1,2] will need a "3" appended. Likewise then if the item with an ID of "2" is unselected, it will likewise be removed from the query string leaving the new string as sources[1,3]
I am somewhat new to javascript/jquery and especially more advanced string manipulation. I have been attempting to recreate something to demonstrate this and have gotten to the following which is not fully working.
Basically my initial if statement works as intended, but the moment the else is hit (when another ID needs to be added to an existing model in the query string - like a second ID for [sources] or [codes]) it returns wonky output - seeng as I couldnt get the right formula to update everything correctly.
//TIMEMTABLE QUERY
function updateCalendar(filter_id, filter_element) {
//Run through the filter checks before making final call to query and update timetable?
//GET THE MODEL/OBJECT NAME
var queryName = filter_element.attr('data-owner');
//GET THE IDs //this is either an array of all selected IDs or a single id which is used in the else statement
var queryId = filter_element.attr('value');
var queryIds = $('#'+filter_id).val();
var modelCheckIndex = requestString.toLowerCase().indexOf(queryName.toLowerCase());
//build a request string
if (modelCheckIndex < 0) {
console.info('ADD MODEL TO QUERY STRING');
requestString = requestString + "&" + (queryName.toLowerCase() + "[" + queryIds + "]");
console.log(requestString);
}
else{
console.info('UPDATE MODEL ON QUERY STRING');
var position = requestString.toLowerCase().indexOf(queryName.toLowerCase());
//requestString = requestString.substr(modelCheckIndex -1, requestString.length -1) + "," + queryId + "]";
requestString = requestString.slice(modelCheckIndex.indexOf("]"), modelCheckIndex) + "," + queryId;
console.log(requestString);
}
//MAKE THE API CALL USING CREATED QUERY STRING
}
If anyone has any examples or fiddles lying around I would also appreciate it.
Fiddle I am trying to get to work
It looks like you are just having trouble parsing and updating a query string. In which case, I have a function I've been using for that (thank you Google)
function getUriParams(string) {
var params = {},
queryString = string.slice(string.lastIndexOf('?')).substring(1),
regex = /([^&=]+)=([^&]*)/g,
m;
while (m = regex.exec(queryString)) {
params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
}
return params;
}
The input is your requestString and the output is an object of key value pairs of the query string.
To make the object a string, jQuery makes it easy with $.param().
//get key value pairs
var obj = getUriParams(requestString);
//make query string
var str = $.param(obj);
I suggest to change the logic a bit. I would use some data storage for the wanted parameter and rebuild the request string every time when it's necessary, like the below example.
It is much more better, than rebuild the string each time when some value has changed.
var data = {
sources: [1, 2],
plans: [],
codes: [1, 3, 4]
};
function buildStr() {
function get(key) { return key + '=' + JSON.stringify(data[key]); }
return '/api/bla?' + ['sources', 'plans', 'codes'].map(get).join('&');
}
document.write('<pre>' + buildStr() + '</pre>');

Categories

Resources