highlight the search text in prime-ng autocomplete - javascript

I am using the PrimeNG AutoComplete with angular 7.
In the auto complete I return a list of all the
items that their
description contains the search text.
I would like to highlight matched words in each element of the shown list.
<p-autoComplete
#autoComplete
(onFocus)="autoComplete.handleDropdownClick()"
[suggestions]="results"
(completeMethod)="search($event)"
[multiple]="true"
field="Path"
(onSelect)="selectedValues($event)"
(onUnselect)="unSelectedValues($event)"
placeholder="Select Domain"
emptyMessage= {{noResult}}
[formControl]="dataModelControl"
>
<ng-template let-value pTemplate="item">
<div class="ui-helper-clearfix">
<span [innerHTML]="value.Path | highlight :
toHighlight"></span
</div>
</ng-template>
</p-autoComplete>
This is the definition of the highlight pipe:
#Pipe({ name: 'highlight ' })
export class HighlightPipe implements PipeTransform {
transform(text: string, search): string {
const pattern = search
.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&")
.split(' ')
.filter(t => t.length > 0)
.join('|');
const regex = new RegExp(pattern, 'gi');
return search ? text.replace(regex, match => `<b>${match}</b>`) :
text;
}
}
}
//in ts file
search(event) {
if (event.query) {
this.toHighlight = event.query;
this.results = this.data.filter(option => {
return option.Path.toLowerCase().indexOf(event.query.toLowerCase())
>= 0
});
} else {
this.results = this.data.slice();
}
}

Related

Angular Unordered Sentence Search

In my code, I have a search bar and I'm trying filter elements of a list according to what I have written. The filter works fine if I search the element in an order. For example, if I search 'red blue yellow' like 'red blue' but, I want the filter work when I search it as 'red yellow' too. Here is my code, what should I add to achieve what I want?
HTML:
<input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter">
TS:
dataSource: MatTableDataSource<IProduct>;
#ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
#ViewChild(MatSort, { static: true }) sort: MatSort;
filterPredicate(data: IProduct, filter: string) {
let fullText = "";
fullText += data.StockIntegrationCode;
fullText += data.ProductGroup ? data.ProductGroup.GroupName : "";
fullText += data.ProductCategory
? data.ProductCategory.CategoryName
: "";
fullText += data.ProductName;
fullText += data.Description;
fullText += data.UnitType ? data.UnitType.Name : "";
fullText += data.IsActive ? "Aktif" : "Taslak";
return (fullText as any).toLocaleLowerCase("tr").indexOf(filter) >= 0;
}
applyFilter(filterValue: string) {
if (this.dataSource) {
this.dataSource.filter = filterValue.trim().toLocaleLowerCase();
if (this.dataSource.paginator) {
this.dataSource.paginator.firstPage();
}
}
}
Imagine you can use
fullText=fullText.loLocaleLowerCase("tr")
return filter.split(' ').filter(x=>(fullText.indexOf(x)<0)).length==0
That, split your "filter", then you has, e.g. ["red","yellow"] and filter the array only the elements that it's NOT in the fullText.
If there're none (length==0) is because all are in the fulltext.
NOTE: I write the fullText=fullText.loLocaleLowerCase("tr") to not repeat for each "word" in your filter.

Styling a specific word in a data gotten from an api which is populated on a screen

After sending a to an API with a 'search value' that searches the database for the value and respond with data containing the value. I want to highlight the given word or search value in the response. The respond is populated to the UI
<center>
{Object.keys(Disp).map((key, i) => (
<center key={i} className="PublicationCard">
<span> {key}</span>
<a href={"https://www." + Disp[key]}> View Publication</a>
</center>
))}
</center>
The Key is a sentence but I want to bolden a word in the key
For example, let say the key is "Discussions about Supervised Artificial Intelligence" and the search query was artificial intelligence, all I want to do is to bold the search query that is "artificial intelligence' in the UI
Maybe you can create a function to generate multiple span tags.
function GenerateSpan({ text, search }) {
const reg = new RegExp(`(.*)(${search})(.*)`, "g")
const array = [...text.matchAll(reg)]
if (array.length > 0) {
return array[0].slice(1).map((textPart, index) => {
return <span key={index} className={textPart === search ? "highlight" : ""}>{textPart}</span>
})
} else {
return <span>{text}</span>
}
}
And use in your code :
<GenerateSpan text=key search="Supervised"/>
And then add style for class "highlight
.highlight{
font-weight: bold
}
So Finally :
<center>
{Object.keys(Disp).map((key, i) => (
<center key={i} className="PublicationCard">
<GenerateSpan text=key search="Supervised"/>
<a href={"https://www." + Disp[key]}> View Publication</a>
</center>
))}
</center>
The example with RegExp works faster, but here is a simpler way.
function Highlighter({ highlight = '', children: text }) {
const index = text.toLowerCase().indexOf(highlight.toLowerCase());
if (index >= 0 && highlight.length) {
const beforeHighlight = text.substring(0, index);
const highlightedPart = text.substring(index, index + highlight.length);
const afterHighlight = text.substring(index + highlight.length);
return (
<highlight-text>
{beforeHighlight}
<span>{highlightedPart}</span>
{afterHighlight}
</highlight-text>
);
}
return <>{text}</>;
}
export default Highlighter;
And use it like:
<Highlighter highlight={'Recursion'}>
{'Recursion a misunderstood topic'}
</Highlighter>

Vue2 - Method to highlight text with text input

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!

How to highlight search input when data rendered?

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>

Multiple custom filters in AngularJS

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 );
}
})

Categories