searchStr is user input search keyword , once i rendered response from server i want to highlight user input searchStr so user can see what is being searched and compare if its part of response. So below code is highlighting whole string response from server in my case i just want to highlight searched string that will be part of response.
Lets assume i have string
info|<n/a>|[routes.event] ########## Message added to processing queue ########## e63637db-aa33-4aed-b5b0-51a0764dc7f1 { workerId: 3, pid: 33029 } and i want to highlight e63637db-aa33-4aed-b5b0-51a0764dc7f1 _id that will be searchStr
main.html
<tr ng-repeat="item in showMessages | filter:searchStr" >
<td >{{item.filename}}</td>
<td class="serverResults" ng-bind-html="item.value | trusted">{{item.value}}</td>
</tr>
ctrl.js
$scope.$on('displaySearchResults',function(e,data){
$scope.searchStr = data.searchStr;
$scope.showMessages = data.messageObj;
})
filters.js
angular.module('App').filter('trusted', ['$sce', function ($sce) {
return function(text,phrase) {
if (phrase) text = text.replace(new RegExp('('+phrase+')', 'gi'),
'<span class="highlighted">$1</span>');
var content = text.toString()
console.log('Content',content);
var data = content.replace(/[|&;$%#"<>()+,]/g, "");
return $sce.trustAsResourceUrl(data);
};
}]);
Here is a working sample to show how you might accomplish the highlighting. It is contrived because I'm just creating an array with a single item, but it illustrates the approach. You want to apply your replacement of reserved characters first because if you apply that after you have inserted the highlighted <span> the < and > characters will be stripped by your replacement regex.
angular.module('app', [])
.controller('ctrl', function($scope) {
$scope.showMessages = [{
value: 'info|<n/a>|[routes.event] ########## Message added to processing queue ########## e63637db-aa33-4aed-b5b0-51a0764dc7f1 { workerId: 3, pid: 33029 }'
}];
$scope.searchStr = 'e63637db-aa33-4aed-b5b0-51a0764dc7f1';
})
.filter('trusted', function($sce) {
return function(text, phrase) {
if (phrase) {
var data = text.replace(/[|&;$%#"<>()+,]/g, "");
data = data.replace(new RegExp('(' + phrase + ')', 'gi'),
'<span class="highlighted">$1</span>');
return $sce.trustAsHtml(data);
}
text = text.replace(/[|&;$%#"<>()+,]/g, "");
return $sce.trustAsHtml(text);
};
});
.highlighted {
background-color: yellow;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<div>Search: <input type="text" ng-model="searchStr" /></div>
<div>
<table>
<tr ng-repeat="item in showMessages | filter:searchStr">
<td>{{item.filename}}</td>
<td class="serverResults" ng-bind-html="item.value | trusted:searchStr"></td>
</tr>
</table>
</div>
</div>
Related
I'm trying to highlight a text when it matches the text entered in a text input.
So if I have this data
data: function() {
return {
names:['John', 'Johan', 'Diego', 'Edson']
searchFilter:''
}
}
And this html:
<input type="text" v-model="searchFilter">
<div v-for="b in names">
<p v-html="highlight(b)"></p>
</div>
If I type "Joh" in the input, I would like to get in my html:
John
Johan
Diego
Edson
<div>
<p><strong>Joh</strong>n</p>
<p><strong>Joh</strong>an</p>
<p>Diego</p>
<p>Edson</p>
</div>
So far, I have written this method, but it highlights all the word, not just the typed characters.
methods: {
highlight(itemToHighlight) {
if(!this.searchFilter) {
return itemToHighlight;
}
return itemToHighlight.replace(new RegExp(this.searchFilter, "ig"), match => {
return '<strong">' + match + '</strong>';
});
}
}
Any advice would be great. Thanks!
Rough proof of concept
You could do something like this:
methods: {
highlight(itemToHighlight) {
if(!this.searchFilter) {
return itemToHighlight;
}
return itemToHighlight.replace(new RegExp(this.searchFilter, "ig"), match => {
return '<strong">' + this.searchFilter + '</strong>' + (match.replace(this.searchFilter, ''));
});
}
}
Essentially, the idea being that you are using the matching search term as a base, then getting the portion that doesn't match by replacing the matched string with nothing ('').
Please note, this wasn't tested, but more of a proof of concept for you. It most likely works.
A working pure JavaScript implementation
function nameMatcher(names, searchTerm) {
return names.reduce((all, current) => {
let reggie = new RegExp(searchTerm, "ig");
let found = current.search(reggie) !== -1;
all.push(!found ? current : current.replace(reggie, '<b>' + searchTerm + '</b>'));
return all;
}, []);
}
let allNames = ['John', 'Johan', 'Deon', 'Ripvan'];
let searchTerm = 'Joh';
console.log(nameMatcher(allNames, searchTerm));
By running the example, you will see that the function nameMatcher correctly replaces the properly matched string within each positive match with the search term surrounded with a <b> element.
A working Vue Implementation
new Vue({
el: ".vue",
data() {
return {
names: ['John', 'Johan', 'Deon', 'Derek', 'Alex', 'Alejandro'],
searchTerm: ''
};
},
methods: {
matchName(current) {
let reggie = new RegExp(this.searchTerm, "ig");
let found = current.search(reggie) !== -1;
return !found ? current : current.replace(reggie, '<b>' + this.searchTerm + '</b>');
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div class="container vue">
<input v-model="searchTerm" placeholder="Start typing here...">
<div v-for="(name, key) in names">
<div v-html="matchName(name)"></div>
</div>
</div>
Let me know if you have any questions! Hope this helps!
http://jsfiddle.net/f4Zkm/213/
HTML
<div ng-app>
<div ng-controller="MyController">
<input type="search" ng-model="search" placeholder="Search...">
<ul>
<li ng-repeat="name in names | filter:filterBySearch">
{{ name }}
</li>
</ul>
</div>
</div>
app.js
function escapeRegExp(string){
return string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
}
function MyController($scope) {
$scope.names = [
'Lolita Dipietro',
'Annice Guernsey',
'Gerri Rall',
'Ginette Pinales',
'Lon Rondon',
'Jennine Marcos',
'Roxann Hooser',
'Brendon Loth',
'Ilda Bogdan',
'Jani Fan',
'Grace Soller',
'Everette Costantino',
'Andy Hume',
'Omar Davie',
'Jerrica Hillery',
'Charline Cogar',
'Melda Diorio',
'Rita Abbott',
'Setsuko Minger',
'Aretha Paige'];
$scope.search = '';
var regex;
$scope.$watch('search', function (value) {
regex = new RegExp('\\b' + escapeRegExp(value), 'i');
});
$scope.filterBySearch = function(name) {
if (!$scope.search) return true;
return regex.test(name);
};
}
From the above example, I have been trying to create a wildcard regex search by using a special character '*' but I haven't been able to loop through the array.
Current output: If the input is di, it showing all the related matches.
Required: What I am trying to get is, if the input is di*/*(any input), it should show all the matches as per the given input.
There are a couple issues with your approach. First, you are escaping * in your escape routine, so it can not be used by the client.
Second, you are not anchoring your lines, so the match can occur anywhere.
To fix, remove the asterisk from the escape function :
function escapeRegExp(string){
return string.replace(/([.+?^=!:${}()|\[\]\/\\])/g, "\\$1");
}
Then in your watch function replace * with .* and add line anchors :
$scope.$watch('search', function (value) {
var escaped = escapeRegExp(value);
var formatted = escaped.replace('*', '.*')
if(formatted.indexOf('*') === -1){
formatted = '.*' + formatted + '.*'
}
regex = new RegExp('^' + formatted + '$', 'im');
});
Here is a fiddle
I want to put 2 customs filters on a data. Each filter is working perfectly independently, but when I put the 2 on the same data, I have an error message (TypeError: input.replace is not a function), but if I comment this, I have another error message (Error: [$sce:itype] Attempted to trust a non-string value in a content requiring a string: Context: html)
The 2 customs filters are goBold which take no argument, and limitHellip which takes the maximum length of the string as an argument.
thanks a lot, here is the code :
angular.module('appFilters', []).
filter('goBold', function($sce) {
return function (input) {
input = input.replace(' filter ',' <strong>WORD FILTERED</strong> ');
return $sce.trustAsHtml( input );
}
}).
filter('limitHellip', function($sce) {
return function (input, limit) {
console.log(limit);
if( input.length > 100 ) {
input = input.substring(0,100);
var index = input.lastIndexOf(" ");
input = input.substring(0,index) + "…";
}
return $sce.trustAsHtml( input );
}
});
<ion-content class="has-subheader">
<ion-item class="element item item-divider" data-ng-repeat="item in blocInfos" >
<a href="#/list/{{ item.url }}">
<img data-ng-src="img/{{ item.imageUrl }}" alt="">
<h2 class="title">{{ item.title }}</h2>
</a>
<p data-ng-bind-html="item.text | limitHellip: 100 | goBold" class="wrap"></p>
</ion-item>
</ion-content>
This is because your first filter return an $sce.trusAsHtml() which is an Object and not a string so you can't call the replace() method.
So, you have to change it into a String
Plunkr
Filters
angular.module('appFilters', []).
filter('goBold', function($sce) {
return function (input) {
input = input.toString(); //This line was added
input = input.replace(' filter ',' <strong>WORD FILTERED</strong> ');
return $sce.trustAsHtml( input );
}
}).
filter('limitHellip', function($sce) {
return function (input, limit) {
input = input.toString(); //Maybe you have to add it here too
console.log(limit);
if( input.length > limit ) {
input = input.substring(0,limit);
var index = input.lastIndexOf(" ");
input = input.substring(0,index) + "…";
}
return $sce.trustAsHtml( input );
}
})
This is going to be a rather longwinded question, so please bear with me...
I have an array of about 25-30 items. They are sorted through various filters such as brand, type, material, size, etc.. How can I go about building a searchable filter. All of the ones I've seen just include a filter:query | in their filters. However I can't get mine to query my existing array.
Here is what my array looks like, only going to show 1 item to keep size down..
$scope.products = [
{
src: 'images/img/image1.jpg',
name: 'XXX-1A',
brand: 'Brand A',
material: 'dry',
size: '00',
type: 'dry pipe',
color:'red'
}];
Function for filtering (only included 1 to save space):
$scope.brandIncludes = [];
$scope.includeBrand = function(brand) {
var i = $.inArray(brand, $scope.brandIncludes);
if (i > -1) {
$scope.brandIncludes.splice(i, 1);
} else {
$scope.brandIncludes.push(brand);
}
}
$scope.brandFilter = function(products) {
if ($scope.brandIncludes.length > 0) {
if ($.inArray(products.brand, $scope.brandIncludes) < 0)
return;
}
return true;
}
This is what I am using to filter from the HTML, I am using checkboxes to select each filter:
<div class="info" ng-repeat="p in products |
filter:brandFilter |
filter:materialFilter |
filter:typeFilter |
filter:styleFilter">
</div>
My search bar mark up:
<div class="filtering">
<div class="search-sect">
<input name="dbQuery" type="text" placeholder="Search pieces" class="search-input" ng-model="query"/>
</div>
One of the filter's mark up:
<input type="checkbox" ng-click="includeStyle('adaptor')"/>Adaptor<br>
Now that you have all the code, here are some of the things I've tried that don't seem to be running right:
My Attempt:
Search bar:
<input type="text" id="query" ng-model="query"/>
Filter:
<li ng-repeat="p in products | filter:query | orderBy: orderList">
I understand that to some experienced with angular, this is a relatively easy task, but I am just learning and can't seem to wrap my head around searching a query. It's probably a simple solution that I am overlooking. This is my first Angular app and I am trying to bite off more than I can chew in order to learn more.
I appreciate all responses, thanks in advance!
As per request: CodePen
The simple built-in angular filter is not smart enough to to work with your checkbox design, so try writing a custom filter. You will need to bind the checkboxes you mentioned to variables in your scope, e.g. brandFilterIsEnabled. See the tutorial for writing custom filters. Here is a working example.
var myApp = angular.module('myApp', []);
myApp.controller('ctrl', function ($scope) {
$scope.items = [{
name:'foo',
color:'red'
},{
name:'bar',
color:'blue'
},{
name:'baz',
color:'green'
}];
$scope.searchNames = true;
$scope.searchColors = true;
$scope.$watch('searchColors', function(){
$scope.searchKeys = [ $scope.searchNames ? 'name' : null, $scope.searchColors ? 'color' : null ];
});
$scope.$watch('searchNames', function(){
$scope.searchKeys = [ $scope.searchNames ? 'name' : null, $scope.searchColors ? 'color' : null ];
});
});
myApp.filter('advancedSearch', function($filter) {
return function(data, keys, query) {
results = [];
if( !query ){
return data;
} else {
angular.forEach( data, function( obj ){
var matched = false;
angular.forEach( keys, function( key ){
if( obj[key] ){
// match values using angular's built-in filter
if ($filter('filter')([obj[key]], query).length > 0){
// don't add objects to results twice if multiple
// keys have values that match query
if( !matched ) {
results.push(obj);
}
matched = true;
}
}
});
});
}
return results;
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<html ng-app="myApp">
<div ng-controller="ctrl">
<input type='checkbox' ng-model='searchNames'>search names</input>
<input type='checkbox' ng-model='searchColors'>search colors</input>
<input type='text' ng-model='query'>search objects</input>
<ul>
<li ng-repeat="item in items | advancedSearch : searchKeys : query">
<span style="color:{{item.color}}">{{item.name}}</span>
</li>
</ul>
</div>
</html>
repet to display results in a input field.
Here is the data (being returned from controller scope is companycodes)
company code
abc 111
abc 10012
abc 6434
xyz 1235
xyz 33
<div ng-repeat="x in companycodes">
<h3>{{x.company}}</h3>
<input type="text" ng-name="code" ng-model=x.code>
</div>
What I am trying to do is that it lists codes under company heading i.e,
abc
111
10012
6434
xyz
1235
33
Can you please let me know how to change the ng-repeat to get the output desired.
Thanks
You can write a filter to group the data by company then repeat the code having the corresponding company
JS
myModule.filter('groupBy', function () {
return function (array, expression) {
var result = [], i = 0;
if (!angular.isArray(array)) {
return array;
}
for (; i < array.length; i += 1) {
var value = array[i][expression];
if (result.indexOf(value) === -1) {
result.push(value);
}
}
return result;
};
})
HTML:
<div data-ng-repeat="company in (companycodes | groupBy: 'company')">
<h3>{{company}}</h3>
<div data-ng-repeat="comp in companycodes | filter: {company: company}">
<input type="text" data-ng-model="comp.code">
</div>
</div>
http://plnkr.co/edit/bhQVDUVJS8Tz7yjaEPaM?p=preview