mark search string dynamically using angular.js - javascript

How can I mark my search pattern dynamically in my html?
Example:
I'm using angular and my html looks like this:
<div>
<input type="text" ng-model="viewmodel.searchString"/>
<!--Moving over all phrases-->
<div ng-repeat="phrase in viewmodel.Phrases">
{{phrase.title}}
</div>
</div>
I want the string matching pattern will be mark on every change in search string.
Can you help me?

Angular UI is a great choice. You can also do it with filter like: http://embed.plnkr.co/XbCsxmfrgmdtOAeBZPUp/preview
The essence is as commented by #Hylianpuffball, dynamically create styled 'span' tags for the matches.
.filter('highlight', function($sce) {
return function(text, phrase) {
if (phrase) text = text.replace(new RegExp('('+phrase+')', 'gi'),
'<span class="highlighted">$1</span>')
return $sce.trustAsHtml(text)
}
})
And use it like:
<li ng-repeat="item in data | filter:search.title"
ng-bind-html="item.title | highlight:search.title">
</li>

Just in case that someone (like me a moment ago) needs this for angular2:
highlight-pipe.ts:
import {Pipe, PipeTransform} from '#angular/core';
#Pipe({name: 'highlightPipe'})
export class HighlightPipe implements PipeTransform{
transform(text:string, filter:string) : any{
if(filter){
text = text.replace(new RegExp('('+filter+')', 'gi'), '<span class="highlighted">$1</span>');
}
return text;
}
}
and use it like this:
at top of file:
import {HighlightPipe} from './highlight-pipe';
in template where 'yourText' is the original text and 'filter' is the part you want to highlight:
<div [innerHTML]="yourText | highlightPipe: filter"/>
in component:
pipes: [HighlightPipe]
EDIT:
I updated it for RC 4
and created a plunker for testing:
http://plnkr.co/edit/SeNsuwFUUqZIHllP9nT0?p=preview

Try Angular UI
They have a highlight directive. You can use it as a reference to make your own (or just use it directly).

Inspired by #tungd's answer but valid for multiple search terms.
.filter('highlight', function($sce) {
return function(text, phrase) {
if (phrase){
phrases = phrase.split(" ");
for(i=0;i<phrases.length;i++)
text = text.replace(new RegExp('('+phrases[i]+')', 'gi'),'~~~~~$1%%%%%')
text = text.replace(new RegExp('('+'~~~~~'+')', 'gi'),'<span class="bold greenTxt">');
text = text.replace(new RegExp('('+'%%%%%'+')', 'gi'),'</span>')
}
return $sce.trustAsHtml(text)
}
});
PS: One can always limit the input to be in non-special chars for this to be 100% bullet-proof.

Related

replace string text with html tag using regex

I've this text in string:
let text = "#Jim, Start editing to see some magic happen!";
how can I want to make it to?
text = "<span style="color:red">#Jim</span>, Start editing to see some magic happen!";
my failed attempt as below:
export default function App() {
let text = "#Jim, Start editing to see some magic happen!";
// const regex = /#|\[|\]/gm;
// text = text.replace(regex, (innerText) => {
// return <span style={{ color: "red" }}>{innerText}</span>;
// });
return (
<div className="App">
<div
dangerouslySetInnerHTML={{
__html: text
}}
/>
</div>
);
}
https://codesandbox.io/s/youthful-gwen-55757z?file=/src/App.js:24-419
I'm not sure this is what you really need. Anyway, I think it's a step closer.
I made a new regex to get '#' + words and for the text to be updated with replace it is necessary to play
text = text
Then:
import "./styles.css";
export default function App() {
let text = "#Jim, Start editing to see some magic happen!";
const regex = /\#[a-zA-Z]+/gm;
text = text.replace(regex, (match) => {
return `<span style="color: red">${match}</span>`;
});
return (
<div className="App">
<div
dangerouslySetInnerHTML={{
__html: text
}}
/>
</div>
);
}
Your approach works, its just that the style is not rendered in innerHTML
Run this example and you'll see that the hashtag appears bold (Also changed your regex a bit)
let text = '#Jim, Start editing to see some magic happen!';
const regex = /(#+[a-zA-Z0-9(_)]{1,})/gm;
text = text.replace(regex, (match) => {
return `<strong>${match}</strong>`;
});
If you want the styles to apply you have to change the Encapsulation in your component to None like so:
#Component({
selector: 'app-my-hashtag-component',
styles: ['./app-my-hashtag-component.css'],
templateUrl: './app-my-hashtag-component.html',
encapsulation: ViewEncapsulation.None,
})

How to parse and populate the value from one field into an another in Angular 2

I have a simple form with two input fields. I would like to take the text from one field and snip the first part and replace it with some other text and populate it into the second field dynamically. I was able to achieve the getting part from the field with ease but I am not sure how to snip the value on keyup. The following is the code:
Component HTML
<div class="labels">Real Name</div>
<div class="lbl">
<input #realName type="text" [(ngModel)]="actualName" (keyup)="trigger()">
</div>
<div class="labels">Cyber Name</div>
<div class="lbl">
<input #cybName type="text"[(ngModel)]="cyberName">
</div>
Component TS
#ViewChild('realName') realName: ElementRef;
#ViewChild('cybName') cybName: ElementRef;
trigger() {
this.cybName.nativeElement.value = this.realName.nativeElement.value;
}
On every keyup, I am setting the value of the cybName with the realName. But, I would like to snip the first 4 characters of the realName and replace it with droid and the remaining characters of the realName i.e., if the typed in real Name was "Alexys", I would like to make it "droidys".
I am sure I shouldn't be doing it with keyup but I am not sure what else to use for this situation. Could anyone help me with it. Thank you.
An easy solution that I think will solve your issue:
Changes in html:
<input #realName type="text" [ngModel]="actualName" (ngModelChange)="trigger($event)">
Changes in ts:
trigger(newActualName) {
this.actualName = newActualName;
this.cyberName = `Droid${event.substring(4)}`;
}
When you update the model (actualName and cyberName) it will also update the input with the new values.
Also: Angular warns against using the ElementRef and accessing the DOM directly. See here for more info
you can use the splice to cut up your input. I am also assuming the following use cases
"AAAAAA" -> "droidAA"
"AAAA" -> "droid"
"AA" -> "AA"
const strLength = this.realName.nativeElement.value && this.realName.nativeElement.value.toString().length;
if (strLength >= 4) {
this.cybName.nativeElement.value = 'droid' + this.realName.nativeElement.value.toString().splice(4, strLength);
} else {
this.cybName.nativeElement.value = this.realName.nativeElement.value;
}
Also, you shouldn't have to use nativeElement. You should be able to use the variables themselves.
if (this.actualName && this.acutualName.length >= 4) {
this.cyberName = 'droid' + this.actualName.splice(4, this.acutualName.length);
} else {
this.cyberName = this.actualName;
}
I think you just want to append the substr:
let cyberName = 'droid';
let realName = 'Alexys';
let cyberDroidName = `${cyberName}${realName.substring(4)}`;
console.log(cyberDroidName);
Try the following:
<div class="labels">Real Name</div>
<div class="lbl">
<input #realName type="text" [(ngModel)]="actualName">
</div>
<div class="labels">Cyber Name</div>
<div class="lbl">
<input #cybName type="text"[ngModel]="'droid' + actualName.substring(4)">
</div>
As you can see by all the answers there are many ways to skin this Cat. If it were me, I would use a pipe to achieve this. As shown in this working example.
You can create a pipe like this:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({name: 'robotNamePipe'})
export class RobotNamePipe implements PipeTransform {
transform(value: number, exponent: string): string {
// Run your logic in here
return `${value} - Anyhting you want!`;
}
}
... and then in your HTML use it like this:
<div class="labels">Real Name</div>
<div class="lbl">
<input #realName type="text" [(ngModel)]="actualName">
</div>
<div class="labels">Cyber Name</div>
<p>Robot name: {{realName.value | robotNamePipe}}</p>
Because really, you probably don't want people to be able to edit the generated droid name anyway - do you?

How to replace a string with a component (vue)

I have strings that contains ### and I am replacing with array values. Now I want to use them with a component, I created the component and it works but I don't know how to use it in the strings. I don't want to wrap them manually because I don't know how the strings will be, it can have several ###. If it has 2 ###, options will have 2 subArrays.
What is the better way to do it?
Code: https://jsfiddle.net/tsobh4nu/
Vue.component('opt', {
template: `<label>
<span class="bold" v-for="(str,idx) in options">
{{str + " / "}}
</span>
</label>`,
props:{options: Array}
})
new Vue({
el: '#app',
data: {
str: "I have ### and you have a ###.",
options: [
['AAA', 'BBB', 'CCC'],
['XXX', 'YYY', 'ZZZ']
]
},
computed:{
replacedStr(){
let newStr = this.str;
this.options.forEach(option=>{
newStr = newStr.replace('###',this.concatenateOptions(option));
})
return newStr;
}
},
methods: {
concatenateOptions(strArr) {
let separator = "";
let strOptions = "";
strArr.forEach(word => {
strOptions += separator + word;
separator = " / ";
});
return strOptions;
}
}
})
.bold {
font-weight: bold
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
<p>I want something like this, but using components: </p>
{{replacedStr}}
<br>
<hr>
My Components:<br>
<opt :options="options[0]"></opt>
<br>
<opt :options="options[1]"></opt>
</div>
Many thanks.
This is more general, but I hope it will help someone. Add a dynamic component in your template: <component v-bind:is="processedHtml"></component>.
Then add a computed method:
computed: {
processedHtml () {
let html = this.html.replace('[Placeholder]', '<my-component></my-component>');
return {
template: '<div>' + html + '</div>'
}
}
}
Where <my-component> is your custom component and the this.html is your HTML content that contains the placeholder [Placeholder].
It is important to return an element that has one root node. That's why the return is wrapped with <div>.
Read more advanced tutorial about this issue here in my blog. For example, to pass props to <my-component>
I just experienced the same issue. I had an element that needed to display the current count of an item. The current count came from the store and was constantly changing. I used v-text. I know this is pretty situation specific, but hopefully it helps someone down the line.
<P id="results_count" v-text="current_count"></P>
and in the data portion of the component I had a property named current_count that was updated via methods.

Bold matching characters in AngularJS View

When I do a search in my application, I want to wrap the matching characters in the results with bold tags so you can see the matches.
So the results view looks like:
<ul class="search-results ng-hide" ng-show="(results | filter: filterQuery).length > 0">
<li ng-repeat="result in results | filter:filterQuery">
<h3><a ui-sref="{{result.state}}">{{result.name}}</a></h3>
<p>{{result.snippet}}</p>
</li>
</ul>
And the controller:
myApp.controller('SearchCtrl', function($rootScope, $scope, $state, Result, $location, $filter) {
$scope.query = ($state.includes('search') ? $location.search()['q'] : '');
$scope.filterQuery = ($state.includes('search') ? $location.search()['q'] : '');
$scope.results = [];
$scope.queryChanged = function () {
$scope.filterQuery = $scope.query;
if($scope.query){
$state.go('search', {'q': $scope.query} );
} else {
$location.search('q', null);
}
}
if($scope.query){
$scope.results = Result.query();
} else {
$location.search('q', null);
}
});
So I need to wrap tags around the result.name and result.snippet when it matches the filterQuery.
Something like (bits of this were copied from a PHP version I've done in the past, hence the mismatched syntax):
var keys = $scope.filterQuery.split(" ");
result.snippet.replace('/('.implode('|', keys) .')/iu', '<b>\0</b>');
But where would this go?
Create a search-result directive to wrap each search result, where name needs to be set as bold:
<search-result result="result" name="name" ></search-result>
With the following template:
directive.template: 'prefix text' + '<B>' + attrs.name + </B> + ' suffix text';
There's a few ways you could do this, mostly using filters.
You could write your own filter, that would take the string in the search, find it in the content that you are searching, and then add the tags around it and send it back.
Or, you can use one of the many plugins out there that does this.
Heres a question that talks about it
Angular UI Highlight

replace bracket with HTML less-than character

I use AngularJS and have this code:
return text.replace("[", "<");
I expect it to add the < as a HTML character but it does not. It adds it as visible text. That way it print out the HTML code on the page.
How do I replace the less-than character <, without converting it to a text character?
Content of text
Before replace...
[div class="rating-container"]
HTML code
I added this, it's a value and a filter.
{{post.rating.html | replace_brackets }}
I tried to wrap it with
<div ng-bind-html-unsafe="post.rating.html | replace_brackets"></div>
but it just gave white nothing. No errors but no output.
Try this out
Working Demo
html
<div ng-app='MyApp' ng-controller="PostsCtrl">
<div ng-bind-html-unsafe="test | replace_brackets"></div>
</div>
script
var app = angular.module("MyApp", [])
.filter('replace_brackets', function($compile){
return function(text) {
var output = text.replace(/\[/g,'<').replace(/\]/g,'>');
return output;
};
});
app.controller("PostsCtrl", function($scope) {
$scope.test = "[div class='rating-container']Visible[/div]";
});

Categories

Resources