AngularJS search multiple keywords using one field - angularjs

I am new to AngularJS. I have 3 input fields to enter up to 3 keywords for a single post:
<input type="text" ng-model="Keyword1"></input>
<input type="text" ng-model="Keyword2"></input>
<input type="text" ng-model="Keyword3"></input>
I am displaying the posts using ng-repeat:
ng-repeat="post in posts | filter: searchKeyword"
...
and searching by:
Search Keyword: <input ng-model="searchKeyword.Keyword1">
As you can see, this currently only searches the first keyword of every post. How can I have only one search field that searches all three keywords of a post? I know I cannot just do
<input ng-model="searchKeyword.Keyword1.Keyword2.Keyword3">
Thanks!

Not sure if the intention is to return all posts that match at least one keyword or all posts that match all keywords. Here is how you could create a custom filter for matching at least one word:
<div ng-controller="theController">
<input ng-model="inputText"/>
<!-- Use a custom filter to determine which posts should be visible -->
<ul>
<li ng-repeat="post in posts | filter:myFilter">{{post}}</li>
</ul>
</div>
angular.module('theApp', [])
.controller('theController', ['$scope', function($scope) {
$scope.inputText = 'test';
$scope.posts = [
'test test2;lksdf asdf one asdf poo',
'arm test test2 asdf',
'test head arm chest'
];
$scope.myFilter = function(post) {
// default to no match
var isMatch = false;
if ($scope.inputText) {
// split the input by space
var parts = $scope.inputText.split(' ');
// iterate each of the words that was entered
parts.forEach(function(part) {
// if the word is found in the post, a set the flag to return it.
if (new RegExp(part).test(post)) {
isMatch = true;
}
});
} else {
// if nothing is entered, return all posts
isMatch = true;
}
return isMatch;
};
}]);
And here is the plunkr: http://plnkr.co/edit/d17dZDrlhY2KIfWCbKyZ?p=preview
NOTE: this solution does not limit the keywords to 3. Any number of keywords can be entered, separate by the space character. If nothing is entered (or just spaces), everything is returned.

Here is what i will do
write a custom filter (https://docs.angularjs.org/guide/filter)
in filter split the input by space (which will give you separate words)
search all words in input one by one

Related

AngularJs Auto Complete Search

So this works with static data, but when I push data with a $http this autocomplete does not work. The data pushes to the empty array of airport_list but something is happening when I try to use airport_list in for the autocomplete. Not sure what is is. I can only find answers which pertain to static data.
This is updated per everyones help.
Here is the controller
app.controller('selectCtrl', function($scope, $http) {
$scope.airport_list = null;
$http({
url: 'someUrl.com',
method: 'GET'
})
.then((response) => {
angular.forEach(response.data.airports, function(value, key) {
$scope.airport_list = response.data.airports;
})
$scope.airports = $scope.airport_list;
});
$scope.selectAirport = function(string) {
$scope.airport = string;
$scope.hidelist = true;
};
})
Here is the template
<div class="control">
<div>
<input
type="text"
name="airport"
id="airport"
ng-model="airport"
ng-change="searchFor(airport)"
placeholder="From..."
/>
<div class="airport-container-dropdown" ng-hide="hidelist">
<div
class="airport-list"
ng-repeat="airport in airports"
ng-click="selectAirport(airport)"
>
{{ airport.name }}
</div>
</div>
</div>
</div>
I really would like to do this without using bootstrap typeahead.
Thank you for looking at this.
I have made changes as recommended by below answers and the $http request is feeding into the autocomplete as a whole list but searching by name does not work and clicking on name sets [object, object]
this would be the code which is specific to that functionality.
$scope.searchFor = function(string) {
$scope.hidelist = false;
const output = [];
angular.forEach($scope.airport_list, function(airport) {
if (airport[0].toLowerCase().indexOf(string.toLowerCase(airport)) >=
0) {
output.push(airport);
}
});
$scope.airports = output;
};
$scope.selectAirport = function(string) {
$scope.airport = string;
$scope.hidelist = true;
};
Try this:
$scope.airport_list = response.data.airports;
What I am seeing is that you have an array: $scope.airport_list = [];
When you make your http request, you push what I would understand to be an array of airports into that array. So you end up with your airport array from the backend at the first position of $scope.airport_list, vs. $scope.airport_list being the actual list.
For your search method, you should change the following:
In your HTML:
ng-change="searchFor(airport.name)"
In your JS:
I've renamed your function and changed the input variable to be more clear. You were passing in a full airport, but treating it as a string. You need to compare your provided airport name to that of the airports in the array. So you iterate over the array, and compare each element's name property to what you pass in.
$scope.searchFor = function(airportName) {
$scope.hidelist = false;
const output = [];
angular.forEach($scope.airport_list, function(airport) {
if (airport.name.toLowerCase() === airportName) {
output.push(airport);
}
});
$scope.airports = output;
console.log($scope.airports);
};
I have provided minimal changes to your code to implement this, however I suggest you look at this SO post to filter drop down data more appropriately.
Angularjs Filter data with dropdown
If you want to simply filter out what is displayed in the UI, you can try this in your HTML template. It will provide a text field where you supply a partial of the airport name. If at least one character is entered in that box, the list will display on the page, with the appropriate filtering applied. This will avoid having to call functions on change, having a separate array, etc.
<input type="text" name="airport" id="airport" ng-model="airportSearch.name" placeholder="From..." />
<div class="airport-container-dropdown" ng-hide="!airportSearch.name">
<div class="airport-list"
ng-repeat="airport in airport_list | filter:airportSearch"
ng-click="selectAirport(airport)">
{{ airport.name }}
</div>
</div>

AngularJS Ng-Repeat Search Filter, How can i limit what properties to search for?

Currently i have a ng-repeat that writes out a rather large object per row. However, i am only wanting the search filter to scan through 5 properties and not the 20 properties the object has. Is there a way i can tell the filter to only check specific properties and ignore the rest? I was thinking something below...
<div> <input ng-model="search" type="text"/> </div>
<div ng-repeat="item in items | filter:search:property1:property2">
{{item.property1}}
</div>
Create a custom filter that allows you to have necessary parameters as you need.
app.filter('myFilter', function(){
// Just add arguments to your HTML separated by :
// And add them as parameters here, for example:
// return function(dataArray, searchTerm, argumentTwo, argumentThree) {
return function(dataArray, searchTerm) {
// If no array is given, exit.
if (!dataArray) {
return;
}
// If no search term exists, return the array unfiltered.
else if (!searchTerm) {
return dataArray;
}
// Otherwise, continue.
else {
// Convert filter text to lower case.
var term = searchTerm.toLowerCase();
// Return the array and filter it by looking for any occurrences of the search term in each items id or name.
return dataArray.filter(function(item){
var termInId = item.id.toLowerCase().indexOf(term) > -1;
var termInName = item.name.toLowerCase().indexOf(term) > -1;
return termInId || termInName;
});
}
}
});
Then in HTML
<tr ng-repeat="item in data | myTableFilter:filterText:argumentTwo:argumentThree">
Inspired from filter-by-multiple-columns-with-ng-repeat
Not sure you can do directly . I think you will have to write a function in your controller to do that .
e.g.
<div> <input ng-model="search" type="text"/> </div>
<div ng-repeat="item in items | filter:searchSpecificPpts()">
{{item.property1}}
</div>
in your controller create a function like
$scope.searchSpecificPpts(){
//Here goes the logic to filter
return filteredList;
}

Strip characters from ng-repeat filter?

I'm writing an Angular app that will read a magnetic stripe card from a USB device. When I swipe a test card, I get a string back containing the card number. For example, ;12345?, where 12345 is the card number.
The data my app uses doesn't include these "control characters", so I'd like to strip them out of the search string if the string starts with a ; and ends with a ?.
When I write a custom filter:
angular.module('app.filters', [])
.filter('stripcardcontrolcharacters', function() {
return function(text) {
if(text.substring(0, 1) === ";" && text.substring(text.length - 1) === "?") {
return text.substring(1, text.length - 1);
}
};
});
It fails because I'm ng-repeating over an array, and not the string that I've searched for.
How would I get what string I'm filtering for and strip the characters from it?
EDIT: Current suggestion is to use a filter to modify the array to ADD in the control characters so filter: can find it. I might go with that for now, but I'm still curious to know if you can write such a filter
You're passing the entire array to your filter via
ng-repeat="user in users | stripcardcontrolcharacters ...
If that's how you want it to work, you would need to treat it as an array, for example
return function(textArray) {
var invalidChars = /\D/g; // just an example
return textArray.map(text => {
console.log(text);
return text.replace(invalidChars, '');
});
}
You are probably applying the filter to the array, not the string itself.
Look at this example:
angular.module('test', [])
.controller('testController', function($scope){
$scope.names = ['John Doe', 'Jane Doe'];
})
// The test filter
.filter('strip', function(){
return function(str) {
return str.substring(1, str.length - 1);
};
});
And here is how to use it:
<body ng-app="test" ng-controller="testController">
<p ng-repeat="name in names">
{{name | strip }}
</p>
</body>
Note that I'm applying the filter where I use the value, not in the ng-repeat statement.
And here is the working plunker

Angular-UI typeahead show on certain character

I would like to use Angular-UI typeahead directive to do something like Twitter's Tweet composer: show it only when the user inputs a certain character, like # or {, and when a match is selected, append only the selected value, not replace the entire model.
Is it possible with the current Angular-UI implementation?
This is what I achieved using a custom directive:
http://plnkr.co/edit/9eEq6fOZgVWlhBqUXpV5?p=preview
All you need to do is:
<input type="text" ng-model="model"
typeahead-on="{" typeahead-append="}"
typeahead="suggestion for suggestion in ['second', 'minute', 'hour', 'day','week', 'month', 'year']" />
I don't think this is available out the box but the typeahead API allows custom get and format functions to be specified.
Here is a Plunker loosely based on the async example in the angular-ui documentation. My version only kicks in when an # symbol is present in the input value. A custom search is then carried out using the substring after the # sign.
HTML:
<input type="text" ng-model="selected" typeahead="address for address in getLocation($viewValue)" typeahead-input-formatter="formatResult($model)" class="form-control">
Controller:
var prefix = "";
$scope.getLocation = function(val) {
var pos = val.indexOf("#");
if(pos <=0 || pos == val.length-1) {
return [];
}
prefix = val.substr(0,pos); //cache the prefix
var search = val.substr(pos+1); //get the search string
//filter the results
return $filter('filter')(states, search)
};
$scope.formatResult = function(model) {
if(!model) {
return prefix;
}
return prefix + "#"+model;
}
UPDATE
Updated plunk which allows multiple tokens. You can use whatever token matching scheme you want here. This is just an example:
http://plnkr.co/edit/RjSZ2wgI1POtNfbQ6tvy?p=preview

Build a list using Bootstrap ui-typeahead

I'm trying to build a tags input field exactly like the one on this site. The user can start typing tags, the ui-typeahead returns a list from which the user selects a single result, adding it to the list of tags already in the input field.
I started by simplifying the problem to a string concatenation, instead of a list. I can't even get that to work. This is where I've got to, but it doesn't concatenate with the existing value of the field, it replaces it:
<input class="form-control" type="text" ng-model="program.Demographs"
typeahead="d for d in program.providers.Demographs"
typeahead-min-length='3' typeahead-items='10'
typeahead-editable="false"
typeahead-on-select="addItem(program.Demographs)">
Here is the function that should concatenate the string:
$scope.addItem = function (item) {
$scope.program.Demographs = $scope.program.Demographs + item;
};
Can anyone offer any hints or advice?
Try this instead of concatenating;
$scope.program.Demographs = [];
$scope.addItem = function (item) {
$scope.program.Demographs = $scope.program.Demographs.push(item);
};
I thought I'd answer this myself after figuring out that it is not possible to add items to the existing selection in the typeahead input field. Instead, you have to add to a different string or array, not the one bound to the typeahead. Here is an example:
$scope.addDemograph = function (item) {
if ($scope.program.Demographs.indexOf(item) == -1) {
$scope.program.Demographs.push(item);
}
$scope.demographs_input = [];
};
<input class="form-control" type="text" placeholder="Demographs"
ng-model="demographs_input"
typeahead="d for d in program.providers.Demographs"
typeahead-on-select="addDemograph(demographs_input)"> // adds input val to list
<ul class="list-inline">
<li ng-repeat="d in program.Demographs"></li> <!--this list is added to automagically-->
</ul>

Resources