Algolia search: access results onclick - javascript

I am using algolia search for my rails app, I sat up the auto completion method using typehead.js. But I can't redirect properly when I have uppercase in the URL... Here is my code:
// replace YourIndexName by the name of the index you want to query.
var index = algolia.initIndex('Pin');
// Mustache templating by Hogan.js (http://mustache.github.io/)
var template = Hogan.compile('<div class="hit">' +
'<a href="http://yhumans.com/{{{twitter}}}">'
+
'<div class="small-text">' +
'{{{ _highlightResult.name.value }}} ' +
'</div>' +
'<div class="small-text">' +
'#' +
'{{{ _highlightResult.twitter.value }}} ' +
'</div>' +
'</a>' +
'</div>');
// typeahead.js initialization
$('#user-search').typeahead(null, {
source: index.ttAdapter({ hitsPerPage: 5 }),
displayKey: 'twitter',
templates: {
suggestion: function(hit) {
// select matching attributes only
hit.matchingAttributes = [];
for (var attribute in hit._highlightResult) {
if (attribute === 'name' || attribute == 'twitter') {
// already handled by the template
continue;
}
// all others attributes that are matching should be added in the matchingAttributes array
// so we can display them in the dropdown menu. Non-matching attributes are skipped.
if (hit._highlightResult[attribute].matchLevel !== 'none') {
hit.matchingAttributes.push({ attribute: attribute, value: hit._highlightResult[attribute].value });
}
}
// render the hit using Hogan.js
return template.render(hit);
}
}
});
The problem is with this lines that I am using to let the user click on results to access page:
<a href="http://yhumans.com/{{{twitter}}}">'
In fact some of my users has capitalize letters in their twitter usernames, So I can't redirect properly to their profiles on search results.
Let me explain: http://yhumans.com/myname is a correct URL. http://yhumans.com/MyName is a wrong URL
I tried to use lowercase for that variable: "twitter". But I could not find a way to do it properly.
I know that one way to do it would be to lowercase the twitter variable. But the thing is that the function 'downcase!' doesn't seems to works in js.
Any ideas ?

First, two comments that won't answer the question directly, but are related:
with Hogan templates, you should use {{variable}} when the text shouldn't have HTML in it, and {{{variable}}} when it should. That's why you should use {{twitter}}.
Algolia has actually forked typeahead.js#0.10 into autocomplete.js, you should have a look at that.
That being said, you gave one solution, even if you are right, .downcase! doesn't exist, it's actually .toLowercase().
In your suggestion function, just lowercase the twitter attribute that way:
function (hit) {
hit.twitter = hit.twitter.toLowerCase();
// ...
}
One issue with that way of handling the autocomplete redirection is that the user won't be able to use his/her keyboard to chose the result. The recommended way with both autocomplete.js and typeahead.js is to use, respectively the autocomplete:selected or typeahead:selected event:
$('#user-search').typeahead(/* ... */).on('typeahead:selected', function (ew, selection, dataset) {
// Redirect to http://yhumans.com/my-twitter-name
window.location.href = 'http://yhumans.com/' + selection.twitter;
});
To display the current selection to the user when he/she hovers or picks the result with the arrows, you can put a different background color with .aa-hint (autocomplete.js) or .tt-hint (typeahead.js).

Related

Using jQuery / Javascript to apply filter function from an external link when linked page loads

This is an extension of questions I asked here: Owl Carousel 2: filtering items, but keep the sort order using Javascript (hope this is ok).
I have a menu which filters items. I want the filter to be applied when clicked from and external page link. So on page X you click FilterA this directs you to page Y and filters the items to FilterA as if you had just clicked FilterA on page Y.
In the ideal world it would simply use a link such as www.example.com/pageY/#filterA.
You can see the live page here.
This is the filter function:
$(document).ready(function () {
function showProjectsbyCatEur(cat) {
var owl = $(".owl8").data('owlCarousel');
owl.addItem('<div/>', 0);
var nb = owl.itemsAmount;
for (var i = 0; i < (nb - 1); i++) {
owl.removeItem(1);
}
if (cat == 'all8') {
$('#projects-copy8 .project8').each(function () {
owl.addItem($(this).clone());
});
} else {
$('#projects-copy8 .project8.' + cat).each(function () {
owl.addItem($(this).clone());
});
}
owl.removeItem(0);
}
$('.owl8 .project8').clone().appendTo($('#projects-copy8'));
$('#project-terms8 a').click(function (e) {
e.preventDefault();
$('#project-terms8 a').removeClass('active');
cat = $(this).attr('ID');
$(this).addClass('active');
showProjectsbyCatEur(cat);
});
});
My filter menu looks like this:
<div id="filter">
<h1 class="title">Eurorack</h1>
<div id="project-terms8">
<ul class="filter">
<li class="filter"><a id="all8" class="active all" onclick="event.preventDefault();" href="#">Show All</a></li>
<li class="filter 3x"><a id="3x" onclick="event.preventDefault();" href="#">Clocks, Logic & CV</a></li>
<li class="filter 2x"><a id="2x" onclick="event.preventDefault();" href="#">Filters & Resonators</a></li>
<li class="filter 1x"><a id="1x" onclick="event.preventDefault();" href="#">Waveform Modifiers</a></li>
</ul>
</div>
So the answers so far have been helpful but not quite solve my issue. If anyone else has any advice that would be great! It seems using # is not helpful as the filter uses the id this just creates anchors down to the filter, so a /?filter=FILTERITEM would be best.
Alternatively a new filter system would be fine. As long as the sort order remains the same and this can be used with the URL as well as buttons.
You can get a url argument with javascript and use that in the filter.
function getURLParameter(name) {return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search)||[,""])[1].replace(/\+/g, '%20'))||null
}
var filter= getParameterByName('filter');
showProjectsbyCatEur(filter);
then make the link something like "www.mysite.com/pageY/?filter=euro".
You have specified a preference to use an appended hashtag, so I'll answer with regard to that.
Assuming somebody makes the following request:
http://www.abstract.pinkpoliceman.com/products/#all8
You can, on DOM ready, retrieve the category/hashtag value and pass it to the function you're already invoking from the click event handler:
$(document).ready(function () {
// INSERT THE DOM READY CODE YOU ALREADY HAVE HERE
var hash = window.location.hash;
if(hash.length > 0)
{
showProjectsbyCatEur(hash.substring(hash.indexOf('#') + 1));
}
});
EXPLANATION:
hash.substring(hash.indexOf('#') + 1)
This will take the hash value of '#1234' and substring it after the point at which the '#' is found. It basically removes the # character.
NOTE:
What the user submits as a hashtag is out of your control. If they don't submit all8, your else will still catch it and try to filter by it. So I suggest you rethink the conditional logic a bit to cater for 'bad' values.
SINCE UPDATE (Request to Use Query String Parameters)
Get the query string parameters:
$(document).ready(function () {
// INSERT THE DOM READY CODE YOU ALREADY HAVE HERE
function getUrlVars()
{
var vars = [], hash;
var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
for(var i = 0; i < hashes.length; i++)
{
hash = hashes[i].split('=');
vars.push(hash[0]);
vars[hash[0]] = hash[1];
}
return vars;
}
var filter = getUrlVars()['filter'];
if(filter.length > 0)
showProjectsbyCatEur(filter);
});
http://jquery-howto.blogspot.co.uk/2009/09/get-url-parameters-values-with-jquery.html
Url could now look like:
http://www.abstract.pinkpoliceman.com/products/?filter=all8
If you want the hashes to work for links on the same page, you can use an event listener to listen for hash changes:
function filterByHash(){
if(window.location.hash)//.match(/#(all8|\dx)/)
showProjectsbyCatEur(window.location.hash.substr(1));
}
$(document).ready(filterByHash);
$(window).on('hashchange',filterByHash);
You can use the regex to filter out invalid hashes.
In addition, if you want to support old browsers, you can add something like this instead of the hashchange event listener:
$('#filter').on('click','a[href^=#]',function(){
setTimeout(filterByHash,0);
});
The setTimeout pushes the function to the end of the event loop so that it's called after the hash changes.
user3915578 wrote a very nice RegExp, but I think it would be helpful to get a better understanding of query strings as a whole.
The "#" symbol is used by browsers to anchor to certain parts of a page (like how bookmarks work on Wikipedia).
location is a built in object in browsers. The location.search property returns the part of the current URL after the "?" symbol. The query string is the proper way to pass string parameters between pages.
So the query string is the way to go. Each one of your links is going to load the page with a different value assigned to filter. The location object itself can be used to cause a redirect whenever its value changes.
$('#project-terms8 a').click(function (e) {
e.preventDefault();
cat = $(this).attr('ID');
// this line causes a redirect to the same page, but with added query string
// to redirect to a separate page, just build the new url as a string
location = location.origin + location.pathname + '?filter=' + cat;
});
In your document ready function, you'll need to call your filter function on every new load, but only if filter has a value. That's where the rest of user3915578 's code should go.
$(document).ready(function () {
// prev code
var filter = getParameterByName('filter');
if (filter) { showProjectsbyCatEur(filter); }
});
You'll have to reset the active li inside your filter function instead of your click function. Hopefully this clears up your implementation confusion.

jQuery - remove li from array with delete image

I'm attempting to make a menu bar that can have <li> elements added and removed. So far so good, but when I try and remove them I'm running into issues. I've toyed with this for a couple hours and now I'm wondering if this whole process could just be made easier (maybe an object?).
Anyways, here's the full code (80 lines), with comments to follow along.
var tabs = $('.accountSelectNav');
var titles = [];
var listItems = [];
// when the page loads check if tabs need to be added to the ul (menu bar)
$(document).ready(function(e) {
if ($.cookie('listItems') != null) {
console.log('not null');
//return "listItems" to it's array form.
listItems = JSON.parse($.cookie('listItems'));
$('.accountSelectNav').append(listItems);
}
});
$('.selectTable td:first-child').on('click', function(e) {
$('#home_select').removeClass('navHighlight');
//grab the text value of this cell
title = $(this).text();
$.ajax({
url:'core/functions/getAccountId.php',
type: 'post',
data: {'title' : title}
}).fail (function() {
alert('error');
}).done(function(data) {
accountId = $.trim(data);
// store values in the cookie
$.cookie('account_id', accountId, {expires : 7});
$.cookie('title', title, {expires : 7});
window.location = ('home_table.php');
});
// make sure the value is NOT currently in the array. Then add it
var found = jQuery.inArray(title, titles);
if (found == -1) {
titles.push(title);
addTab();
}
// make sure the value is NOT currently in the array. Then add it
found = jQuery.inArray(title, listItems);
if (found == -1) {
addListItem();
//place <li>'s in cookie so they may be used on multiple pages
$.cookie('listItems', JSON.stringify(listItems));
};
});
$("body").on("click", ".deleteImage", function (e) {
var removeTitle = $(this).closest('li').find('a').text();
var removeItem = $(this).closest('li')[0].outerHTML;
//remove title from "titles" array
titles = jQuery.grep(titles, function (value) {
return value != removeTitle;
});
//remove <li> from "listItems" array
listItems = jQuery.grep(listItems, function (value) {
return value != removeItem;
});
// this shows the <li> is still in the listItemsarray
console.log(listItems);
// put the array back in the cookie
$.cookie('listItems', JSON.stringify(listItems));
removeTab(this);
});
$("body").on("mouseover", ".accountSelectNav li", function(e) {
$(this).find('.deleteImage').show();
});
$("body").on("mouseleave", ".accountSelectNav li", function(e) {
$(this).find('.deleteImage').hide();
});
function addTab() {
tabs.append('<li class="navHighlight">' + '' + title + '' + '' + '<img src="images/delete.png" class="deleteImage"/>' + '' + '</li>');
};
function removeTab(del) {
$(del).closest('li').remove();
}
function addListItem() {
var s = ('<li class="navHighlight">' + '' + title + '' + '' + '<img src="images/delete.png" class="deleteImage"/>' + '' + '</li>');
listItems.push(s);
}
So you see I have two arrays of equal length that should always be the same length. One stores the title to be displayed in the tab, the other holds the html for the <li> which will be appended to the <ul>. I have no problem removing the title from its array. However removing the <li> from it's array is becoming a rather big hassle. You see when I get the <li> element after its been inflated the html inside does not exactly match what was put in, the browser adds style elements.
Example, the variable "removeItem" represents the html value of the selected <li> I wish to remove. It looks like this:
<li class="navHighlight">Test1<img src="images/delete.png" class="deleteImage" style="display: inline;"></li>
yet the value in my array "listItems" looks like this:
<li class="navHighlight">Test1<img src="images/delete.png" class="deleteImage"/></li>
So my attempt at removing it from my array always fails because they aren't a perfect match.
Now my question is how do I remove this <li> item? Also is there an easier way to do this whole process and I'm just not seeing it?
Thanks for your time.
EDIT
Fiddle by request here
Easiest way I can explain it.
Click the link to the fiddle.
Click any cell in the "App Name" column
This will add a <li> to the <ul> (menu) above of the table
When you hover over the <li> a picture appears
Click the picture
This should remove the <li>, both from the <ul> and from the array listItems
right now it does not
In the process of making this easier to check, I've taken your JSFiddle and did the following:
removed extra console.log and comments
removed interaction with cookies (since I did not have them in the first place, I figured they wouldn't just the first scenario)
After doing so I reached a point (you can see it here) where the desired functionality just works.
I even went ahead and removed the ajax stuff because that alert was driving me crazy. (here)
Since this works fine, my guess is that your issue lies between the lines that I removed.
Your usage of cookies is as follows:
To load existing tabs and add them back again
To save account_id and title, which is not used back again
To persist the listItems after a new item has been added
I then opened up the console with your version of the fiddle and the execution of javascript stops at $.cookie() with the error undefined is not a function.
This clearly indicates that the issue present in the Fiddle is that jQuery.cookie is not present and so those calls are halting the execution of the rest of your script. This also explains why it just started working when I took them out.
I posted the whole process of how I got there to indicate how I trimmed down the problem to specific parts, which is useful to reduce the problem space. When you're out of options and reach a place when you're lost, it's easier to post a question with less code and the specific part of the problem that you've identified. This will help you in finding the issues that you're facing and StackOverflow to provide proper answers to your questions.
Hope it helps!
Here is the solution I came up with. It should be much easier for people to understand than my original post. Although it's a long read it may be worth it, especially for new developers.
The point of this code is to make a menu bar out of an un-ordered list or <ul>. The menu bar needs to be used on multiple pages. So I'll be using cookies.
I start with this code to get a text value from my table.:
$('.selectTable td:first-child').on('click', function(e) {
// This value will be used later for the name of the tab or `<li>` inside our menu bar or `<ul>`
title = $(this).text();
});
Then I place the value in an array. I do this only if the array does not already have this string inside it. I do not want duplicates:
var found = jQuery.inArray(title, titles);
var titles = [];
if (found == -1) {
titles.push(title);
}
Then I store the array into a cookie, using a library like this:
$.cookie('titles', JSON.stringify(titles));
Now when any page loads that needs this menu bar I run this code to check if there are any values:
$(document).ready(function() {
if ($.cookie('titles') != null) {
titles = JSON.parse($.cookie('titles'));
}
});
Now I need to loop through the array. When I loop through the array I have to do 3 things:
1) Grab the string value.
2) Add the html to my new string so it becomes a list item or <li>.
3) Append the newly created <li> to our <ul>.
Like so:
for(var i = 0; i < titles.length; i++) {
var str = titles[i];
var listItem = '<li class="navHighlight">'
+ '<a href="#">'
+ str
+ '</a>'
+ '<a href="#">'
+ '<img src="images/delete.png" class="deleteImage"/>'
+ '</a>'
+ '</li>';
$('.accountSelectNav').append(listItem);
}
Now, if I want to remove this <li> I click the delete image found inside our <li>. What delete image you say? Look at the html I added again. You will see I add an <img> tag in there.
Now delete like so:
$("body").on("click", ".deleteImage", function (e) {
// grabs the text value of my li, which I want to remove
var removeTitle = $(this).closest('li').find('a').text();
// runs through my titles array and returns an array without the value above
titles = jQuery.grep(titles, function (value) {
return value != removeTitle;
});
});
Then I simply place the new array inside my cookie once again. Like this:
$.cookie('titles', JSON.stringify(titles));
And finally I remove the tab like this:
removeTab(this);
function removeTab(del) {
$(del).closest('li').remove();
}
Yay, I'm done. So now, if anyone has a more elegant way of accomplishing this I'm listening. I have no doubt there's a better way, javascript/jQuery isn't even close to my strong point.
The full code can be found here.

AngularJS : Bootstrap typeahead with multiselect

I am using bootstrap multiselect in my angular application. (Bootstrap 2.3)
http://davidstutz.github.io/bootstrap-multiselect/index-2-3.html#examples
I want to convert the same thing in to bootstrap typeahead but still the user can multiselect values in search results.
I am using the highlighter function of bootstrap typeahed to add check-boxes to results.
I am referring to the this plnkr http://plnkr.co/edit/szO2An1oslDyGftnshyR?p=preview but I am still unable to make my code work.
Old Code for multiselect:
to check last selected values
$scope.selectOptions = function () {
var index = fetching the index of checked values;
var selectedAttrFound = index>-1;
if (selectedAttrFound == true) {
angular.element("#attr-value-selector")
.multiselect('select', $scope.filterAttributes[index].attributeValues);
}
};
to populate the multiselect
$scope.attrValuesWidget = angular.element("#attr-value-selector")
.multiselect({
numberDisplayed: 2,
enableFiltering: true,
maxHeight: "300",
onChange: function (element, checked) {
$scope.attributeActionValue = {
attribute: $scope.attributeSelected,
value: element.val(),
checked: checked
};
$scope.$apply();
}
})
.multiselect('dataprovider', $scope.configAttributeValuesList);
The Select box
<select id='attr-value-selector' multiple='multiple' ></select>
Although you can easily add checkboxes in your highlighter function..
highlighter: function( item ){
var bond = bondObjs[ item ];
return '<div class="bond">'
+'<input type="checkbox" class="mightBeRequiredToCollectTheSelectedResult">'
+'<img src="' + bond.photo + '" />'
+'<br/><strong>' + bond.name + '</strong>'
+'</div>';
}
The problems are;
typehead.js automatically closes its result after you click one of
the search result. You need to override this first.
Then you also need to list the selected values into the textfield.
If you check typehead.js github issues page, issue #254 mentioned that they don't have plan to support multi select, at least for now.
Now I might get downvoted for this, but If I were you I'll consider other alternatives as well.
https://github.com/isteven/angular-multi-select: pure AngularJS, ability to rest selection.
http://ivaynberg.github.io/select2/: rich configuration options
Or the old reliable "chosen" library (I can't post more links)
Hope it helps. Cheers.

Make a javascript function instead variables

Here I have a code that create a sidebar:
var side_bar_html = "<a href='javascript:google.maps.event.trigger(gmarkers[" + parseInt(gmarkers.length - 1) + "],\"click\");'>" + place.name + "</a><br>" + '<div class="raty" />' + "</br>";
$(side_bar_html).appendTo('#side_bar').filter('.raty').raty({
score : place.rating,
path : 'http://wbotelhos.com/raty/lib/img'
})
How I can create a function from this code to create a sidebar with function ...
So something like that:
function Create_a_sidebar_and_put_it_into_ID#sidebar () {
//for every marker to return html string
return "<div class='element'>"+place.name+"</br>"+place.rating+"</div>" + etc...
Becouse I have a problem with creating html, I dont know what to append where and I dont have contol over that
Is it possible?
If I'm understanding your question correctly, you're asking how you can take your first code block that creates a rating for a certain place, and refactor it so that you can arbitrarily create sidebars for places at will. So that's how I'll approach this answer.
As #Sime Vidas mentioned, you can start by taking the code that creates the sidebar itself and making that a function such as that below. I've modified the function a bit to take the javascript out of the href attribute (which is generally considered a bad practice) and replaced passing an html string into $.fn.init (which I've found steeply degrades performance) with using DOM methods to create elements. You also don't need the <br /> after your a element because divs by default are block elements.
function createSidebar(place) {
var $sidebarLink = $(document.createElement('a'));
var $raty = $(document.createElement('div'));
$sidebarLink.attr('href', '#').text(place.name).click(function(evt) {
evt.stopPropagation();
google.maps.events.trigger(gmarkers[parseInt(gmarkers.length - 1, 10)], 'click');
});
$raty.addClass('raty').raty({
score: place.rating,
path: 'http://wbotelhos.com/raty/lib/img'
});
return $([$sidebarLink, $raty]);
}
Now you can do things like
var $sidebar = $('#side_bar');
places.map(createSidebar).forEach(function($sidebarPart) {
$sidebar.append($sidebarPart);
});
Sorry if I'm off track with answering your question, but I think this is what you were asking. If not feel free to leave a comment and we can talk about it more!

Add a second function to this jquery?

The following jQuery function filters my table columns by letter. There is an <a> for each letter. I'm not sure how to add another function though to filter column 1 using a different dropdown on the html side.
JavaScript:
function fil(rexp)
{
$('#tablestyle').dataTable().fnFilter(rexp, 0, true, false);
}
HTML:
<div style="float:left;" class="sortalpha">
ALL
| A
| B
<!-- [...] -->
| Z
</div>
What I have tried to do is copy the top part and change fil to fil2 then copy the HTML part and change those to fil2. Is that the correct way?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Just to give everyone a bit more info, I am using datatables {www.datatables.net} which is a jquery script that presents tables in a nice looking ui with various differnt functions to it like search, filter records per page etc.
I have implemented this mod that someone has listed here >> http://www.datatables.net/forums/discussion/6641/filtering-with-first-letter/p1
It works fine and when I select each letter it filters column 0 using whatever letter I have clicked on. What I am trying to do is have two different filters, one to filter column 0 which is the name of the person, and also another filter that does exaclty the same thing, but for column 1 which is business name, I just wasnt sure how to add the same piece of code twice?.
I don't really know your context, but I would suggest you try using event handlers instead of JavaScript URLs.
So instead of this:
ALL
You could do this:
$('.sortalpha a').on('click', function() {
fil('');
});
Of course, this would make all links filter on ''. To fix that, you could get the text from the <a> that was clicked and use that to call fil(), like so:
function fil(rexp) {
if (rexp.length > 0) {
rexp = '^' + rexp;
}
//$('#tablestyle').dataTable().fnFilter(rexp, 0, true, false);
alert('Filter on: "' + rexp + '"');
}
$('.sortalpha a').on('click', function(event) {
var letter = $(this).text().toLowerCase();
if (letter === 'all') {
fil('');
} else {
fil(letter);
}
});
Here's a working example: http://jsfiddle.net/uzGat/2/
Edit: I updated the answer to account for capital letter and the ^ in the regular expressions.
if the plugin is well written, it must maintain chainbilty so you can do like:
function fil(rexp)
{
$('#tablestyle').dataTable().fnFilter(rexp, 0, true, false).fnFilter(rexp2, 0, true, false);
}

Categories

Resources