Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Data results #79

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ You can also pass a function that receives changes with the `on-type` attribute.

`on-type` : *(optional)* Pass a function that will receive changes, when somebody types something. It passes the full string for any character typed or deleted. You can use that for example to update the array that you passed in data.

`on-select` : *(optional)* Pass a function that will receive changes, when a suggestion is selected. It passes the full string of the suggestion.
`on-select` : *(optional)* Pass a function that will receive changes, when a suggestion is selected. It passes the full string of the suggestion, or the object defined as the suggestion in case you define the suggestions as an array of objects.

`render` : *(optional)* You can use data objects instead of strings to populate the suggestions array. You only have to assign an array of objects to the suggestions collection, and then define a render function that will be used to convert these objects into strings in order for the autocomplete to print them in the list. You can retrieve these objects as the first parameter in the on-select listener. If you use string suggestions you don't have to define this render function but only if you are using data objects as suggestions.

`click-activation` : *(optional)* When `true`, the suggestion box opens on click (unfortunately onfoucs is not implemented properly in most browsers right now). By default it is only activated, when you start typing something.

Expand Down
5 changes: 1 addition & 4 deletions script/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@ app.factory('MovieRetriever', function($http, $q, $timeout){

var moreMovies = ["The Wolverine", "The Smurfs 2", "The Mortal Instruments: City of Bones", "Drinking Buddies", "All the Boys Love Mandy Lane", "The Act Of Killing", "Red 2", "Jobs", "Getaway", "Red Obsession", "2 Guns", "The World's End", "Planes", "Paranoia", "The To Do List", "Man of Steel", "The Way Way Back", "Before Midnight", "Only God Forgives", "I Give It a Year", "The Heat", "Pacific Rim", "Pacific Rim", "Kevin Hart: Let Me Explain", "A Hijacking", "Maniac", "After Earth", "The Purge", "Much Ado About Nothing", "Europa Report", "Stuck in Love", "We Steal Secrets: The Story Of Wikileaks", "The Croods", "This Is the End", "The Frozen Ground", "Turbo", "Blackfish", "Frances Ha", "Prince Avalanche", "The Attack", "Grown Ups 2", "White House Down", "Lovelace", "Girl Most Likely", "Parkland", "Passion", "Monsters University", "R.I.P.D.", "Byzantium", "The Conjuring", "The Internship"]

if(i && i.indexOf('T')!=-1)
movies=moreMovies;
else
movies=moreMovies;
movies = moreMovies;

$timeout(function(){
moviedata.resolve(movies);
Expand Down
93 changes: 76 additions & 17 deletions script/autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ app.directive('autocomplete', function() {
suggestions: '=data',
onType: '=onType',
onSelect: '=onSelect',
render: '=render',
autocompleteRequired: '='
},
controller: ['$scope', function($scope){
Expand Down Expand Up @@ -83,17 +84,41 @@ app.directive('autocomplete', function() {
// selecting a suggestion with RIGHT ARROW or ENTER
$scope.select = function(suggestion){
if(suggestion){
$scope.searchParam = suggestion;
$scope.searchFilter = suggestion;
$scope.searchParam = suggestion.text;
$scope.searchFilter = suggestion.text;
if($scope.onSelect)
$scope.onSelect(suggestion);
$scope.onSelect(suggestion.data);
}
watching = false;
$scope.completing = false;
setTimeout(function(){watching = true;},1000);
$scope.setIndex(-1);
};

//Every time the suggestions collection changes, it will wrap the elements into the wrappedSuggestions:
$scope.wrappedSuggestions = [];
$scope.$watchCollection('suggestions', function(newSuggestions){
if(newSuggestions instanceof Array){
$scope.wrappedSuggestions = newSuggestions.map(function(suggestion, counterIndex){
var renderedText;
if(typeof $scope.render === 'function'){
renderedText = $scope.render(suggestion);
}
else if(typeof suggestion !== 'string'){
console.error('render function must be defined when using data object suggestions');
renderedText = '';
}
else{
renderedText = suggestion;
}
return {
text: renderedText,
data: suggestion,
_id: ''+(counterIndex+1)
};
});
}
});

}],
link: function(scope, element, attrs){
Expand Down Expand Up @@ -214,7 +239,12 @@ app.directive('autocomplete', function() {
index = scope.getIndex();
// scope.preSelectOff();
if(index !== -1) {
scope.select(angular.element(angular.element(this).find('li')[index]).text());
var jLiElement = angular.element(angular.element(this).find('li')[index]);
var suggestionId = jLiElement.attr('data-suggestion-id');
var suggestion = scope.wrappedSuggestions.filter(function(wrappedSuggestion){
return suggestionId == wrappedSuggestion._id;
})[0];
scope.select(suggestion);
if(keycode == key.enter) {
e.preventDefault();
}
Expand Down Expand Up @@ -249,32 +279,61 @@ app.directive('autocomplete', function() {
class="{{ attrs.inputclass }}"\
id="{{ attrs.inputid }}"\
ng-required="{{ autocompleteRequired }}" />\
<ul ng-show="completing && (suggestions | filter:searchFilter).length > 0">\
<ul ng-show="completing && (wrappedSuggestions | myFilter:searchFilter).length > 0">\
<li\
suggestion\
ng-repeat="suggestion in suggestions | filter:searchFilter | orderBy:\'toString()\' track by $index"\
ng-repeat="wrappedSuggestion in wrappedSuggestions | myFilter:searchFilter | orderBy:\'text\' track by $index"\
index="{{ $index }}"\
val="{{ suggestion }}"\
val="{{ wrappedSuggestion.text }}"\
data-suggestion-id="{{ wrappedSuggestion._id }}"\
ng-class="{ active: ($index === selectedIndex) }"\
ng-click="select(suggestion)"\
ng-bind-html="suggestion | highlight:searchParam"></li>\
ng-click="select(wrappedSuggestion)"\
ng-bind-html="wrappedSuggestion.text | highlight:searchParam"></li>\
</ul>\
</div>'
};
});


app.filter('myFilter', function($filter){
return function(wrappedSuggestions, searchFilter){
if(wrappedSuggestions instanceof Array){
searchFilter = searchFilter || '';
return wrappedSuggestions.filter(function(wrappedSuggestion){
var escapeRegexp = function(text) {
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
};
var words = searchFilter.replace(/\ +/g, ' ').split(/\ /g);
var escapedWords = words.map(escapeRegexp); //Make sure non alphanumeric characters are escaped properly before constructing the regexp
//It will detect if all the words of the search are present in the suggestion, no matter the order, nor the case:
var pattern = '';
escapedWords.forEach(function(escapedWord){
pattern += '(?=.*'+escapedWord+')';
});
var rePattern = new RegExp(pattern, 'gi');
var suggestion = wrappedSuggestion.text;
return rePattern.test(suggestion);
});
}
};
});


app.filter('highlight', ['$sce', function ($sce) {
return function (input, searchParam) {
if (typeof input === 'function') return '';
if (searchParam) {
var words = '(' +
searchParam.split(/\ /).join(' |') + '|' +
searchParam.split(/\ /).join('|') +
')',
exp = new RegExp(words, 'gi');
if (words.length) {
input = input.replace(exp, "<span class=\"highlight\">$1</span>");
}
//Hightlight the words or semiwords that are present in both the search and the suggestion:
var escapeRegexp = function(text) {
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
};
var words = searchParam.replace(/\ +/g, ' ').split(/\ /g);
var escapedWords = words.map(escapeRegexp); //Make sure non alphanumeric characters are escaped properly before constructing the regexp
escapedWords.forEach(function(escapedWord){
var wordPattern = '(?!<span[^>]*?>)('+escapedWord+')(?![^<]*?<\/span>)(?=[^>]*(<|$))'; //Match the escapedWord only if it's not already wrapped within span tags, and it's not part of an html attribute or tag name (from previous insertions of span tags into the input)
var wordRegexp = new RegExp(wordPattern, 'gi');
input = input.replace(wordRegexp, "<span class=\"highlight\">$1</span>");
});
}
return $sce.trustAsHtml(input);
};
Expand Down