How to detect if text contain herf link - vue.js

I need to use vue to detect all links in the text like if i have text like this
text :
'hello mate check this link'
i want this text when it buffering in div it appear like this
'hello mate check this link'

The easiest way:
HTML
<span v-html="formattedText"></span>
Script
data() {
return {
text: 'Some kind of text with url www.google.com'
}
},
computed: {
formattedText() {
return this.linkify(this.text)
}
},
methods: {
linkify(inputText) {
const pattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&##\/%?=~_|!:,.;]*[-A-Z0-9+&##\/%=~_|])/gim;
let text = inputText.replace(pattern1, '$1');
const pattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
text = text.replace(pattern2, '$1$2');
return text;
}
It should meet your expectations, you can also change $1 and $2 in >$1|2</a> to link.

Well there are tons of libraries you could use to easily achieve this. But I personally would recommend Anchorme.js due to it's very small size. You can even customize the way the link will get handled when there are #ids & #mentions in text.
Vue.config.productionTip = false;
Vue.config.devtools = false;
new Vue({
el: "#app",
data: function() {
return {
text: `Library works with (http://www.google.com) or without (www.google.com) protocols in link. It works well on paths [www.github.io/something/something] and queries <wordpress.com/post.php?p=112> and even when query starts immediately after the TLDS "wordpress.com?p=112".`,
convertedText: ""
}
},
computed: {
htmlText() {
const converted = anchorme({
input: this.text,
// use some options
options: {
attributes: {
target: "_blank",
class: "detected"
}
},
// and extensions
extensions: [
// an extension for hashtag search
{
test: /#(\w|_)+/gi,
transform: string =>
`${string}`
},
// an extension for mentions
{
test: /#(\w|_)+/gi,
transform: string =>
`${string}`
}
]
});
return converted;
}
}
});
.htmlText {
background-color: #eeeeee;
padding: 1em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://raw.githack.com/alexcorvi/anchorme.js/gh-pages/dist/browser/anchorme.min.js"></script>
<div id="app">
<textarea v-model="text" placeholder="enter text here" rows="3" cols="100">
</textarea>
<br/><br/>
<div class="htmlText" v-html="htmlText"></div>
</div>

Related

How to select text inside input when the input get mounted (Vue3 )

I have this form that I created, basically what its doing is creating a new input with a random value each time I click on ADD_INPUT button. the thing I want is each time I create a new INPUT with random value the value get selected.
Sorry for my very bad English.
I've tried creating a new customized directive like this :
directives: {
focusInput: {
// directive definition
mounted(el) {
el.select()
}
}
},
but it breaks idy
Hoy can use select()
new Vue({
el : '#app',
data : {
text: 'some text'
},
methods : {
select() {
this.$refs.input.select();
},
},
mounted() {
this.select()
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="select">ADD_INPUT</button>
<input type="text" v-model="text" ref="input">
</div>
Composition API
setup() {
const input = ref(null)
function select() {
input.value.select()
}
return {
input,
select
}
}

Format of some words within {{$t('msg')}}

I'm writing a webpage which requires localisation. So I need to put paragraphs into variables:
index.vue
<div class="descrip">
<p class="dTitle">{{$t('message.instructionTitle')}}</p>
<p>{{$t('message.instructionFirst')}}</p>
<p>{{$t('message.instructionSecond')}}</p>
<p>{{$t('message.instructionThird')}}</p>
<p>{{$t('message.instructionForth')}}</p>
<p>{{$t('message.instructionFifth')}}</p>
</div>
index.js
const messages = {
en: {
message: {
instructionFirst: "Hello, world!",
instructionSecond: "...",
//...
},
},
jp: {
message: {
//...
},
},
//..
};
export default messages;
If I want to add some style (e.g. color=red) to some words within the paragraph (e.g. message.instructionFirst), what should I do besides separate those words from message.instructionFirst?
Edit:
For example, instructionFirst is like:
Hello, world!
I want:
Hello, <mark style="color:red;">world</mark>!
As suggested in a comment, you could use a component method to wrap the target words with the styled markup, and use v-html to apply the results:
Create a method (named "highlight") that searches a given string for a word, and returns HTML that surrounds the target words with the desired markup:
export default {
methods: {
highlight(searchWord, input) {
return input.replace(new RegExp(searchWord, 'ig'), w => `<mark style="color:red">${w}</mark>`)
}
}
}
In your template, use the v-html directive to bind the result of highlight() for each translation to a p's innerHTML:
<p v-html="highlight($t('message.instructionFirstHighlight'), $t('message.instructionFirst'))"></p>
demo
I have an idea, how about the following lines:
const messages = {
redKeys: ['instructionFirst'],
en: {
message: {
//...
},
},
jp: {
message: {
//...
},
},
//..
};
export default messages;
then, replace your vue templates with the following lines:
<div class="descrip">
<p
v-for="(value, key, index) in message"
v-key="index"
:class="{
dTitle: key === 'message.instructionTitle'
red: redKeys.includes[key],
}"
>{{$t(value)}}</p>
</div>

Replace tag dynamically returns the object instead of the contents

I'm building an chat client and I want to scan the messages for a specific tag, in this case [item:42]
I'm passing the messages one by one to the following component:
<script>
import ChatItem from './ChatItem'
export default {
props :[
'chat'
],
name: 'chat-parser',
data() {
return {
testData: []
}
},
methods : {
parseMessage(msg, createElement){
const regex = /(?:\[\[item:([0-9]+)\]\])+/gm;
let m;
while ((m = regex.exec(msg)) !== null) {
msg = msg.replace(m[0],
createElement(ChatItem, {
props : {
"id" : m[1],
},
}))
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
}
return msg
},
},
render(createElement) {
let user = "";
let msg = this.parseMessage(this.$props.chat.Message, createElement)
return createElement(
'div',
{
},
[
// "hello",// createElement("render function")
createElement('span', '['+ this.$props.chat.Time+'] '),
user,
msg,
]
)
}
};
</script>
I thought passing createElement to the parseMessage method would be a good idea, but it itsn't working properly as it replaces the tag with [object object]
The chatItem looks like this :
<template>
<div>
<span v-model="item">chatITem : {{ id }}</span>
</div>
</template>
<script>
export default {
data: function () {
return {
item : [],
}
},
props :['id'],
created() {
// this.getItem()
},
methods: {
getItem: function(){
obj.item = ["id" : "42", "name": "some name"]
},
},
}
</script>
Example :
if the message looks like this : what about [item:42] OR [item:24] both need to be replaced with the chatItem component
While you can do it using a render function that isn't really necessary if you just parse the text into a format that can be consumed by the template.
In this case I've kept the parser very primitive. It yields an array of values. If a value is a string then the template just dumps it out. If the value is a number it's assumed to be the number pulled out of [item:24] and passed to a <chat-item>. I've used a dummy version of <chat-item> that just outputs the number in a <strong> tag.
new Vue({
el: '#app',
components: {
ChatItem: {
props: ['id'],
template: '<strong>{{ id }}</strong>'
}
},
data () {
return {
text: 'Some text with [item:24] and [item:42]'
}
},
computed: {
richText () {
const text = this.text
// The parentheses ensure that split doesn't throw anything away
const re = /(\[item:\d+\])/g
// The filter gets rid of any empty strings
const parts = text.split(re).filter(item => item)
return parts.map(part => {
if (part.match(re)) {
// This just converts '[item:24]' to the number 24
return +part.slice(6, -1)
}
return part
})
}
}
})
<script src="https://unpkg.com/vue#2.6.10/dist/vue.js"></script>
<div id="app">
<template v-for="part in richText">
<chat-item v-if="typeof part === 'number'" :id="part"></chat-item>
<template v-else>{{ part }}</template>
</template>
</div>
If I were going to do it with a render function I'd do it pretty much the same way, just replacing the template with a render function.
If the text parsing requirements were a little more complicated then I wouldn't just return strings and numbers. Instead I'd use objects to describe each part. The core ideas remain the same though.

dynamic css not working on checkbox change vue.js

I'm trying to dynamically change the css of some text in a span when a checkbox is checked using v-on:change but it is not changing and I can't figure out why.
The boolean data property "bolden" does change on-click (checked with console.log) but the class does not. the class does not even appear in the 'span' element tag when checked on devtools in chrome
here is a jsfiddle link of a piece of the code: https://jsfiddle.net/rL0fzv7s/
<script src="https://unpkg.com/vue"></script>
<div id="app">
<input type="checkbox" value="food" v-model="blog.categories" v-on:change="bolden = !bolden">
<span v-bind:class="bold">food</span>
</div>
.bold {
font-weight: bolder;
font-size: 25px;
color: red;
}
new Vue({
el: '#app',
data: {
bolden: false,
blog: {
title: "",
content: "",
categories: []
}
},
computed: {
bold: function() {
return {
bolden: this.bolden
};
}
}
})
The reason why your styles are not showing even though the value of bolden is changed is that the class name that you are using is .bold but the class name that you are returning from the computed property is called .bolden.
Change your computed bold functions's return value to the following:
computed: {
bold: function() {
return {
bold: this.bolden // class
};
}
}
You should return bold class while this.bolden true. below is the corrected code. Fiddle : https://jsfiddle.net/62mewykL/
bold: function() {
return {
bold: this.bolden
};
}
OR
Simply use v-bind:class="{'bold' : this.bolden}" in HTML

Custom vue directive to render only if present

Frequently, I want to render a div (or other element) only if it has content. This means repeating the reference to the content in the tag, and in v-if, like this...
<div v-if="store.sometimesIWillBeEmpty">{{store.sometimesIWillBeEmpty}}</div>
With custom directives, I want to create a directive, v-fill, that behaves just like the code above, but with simplified syntax...
<div v-fill="store.sometimesIWillBeEmpty"></div>
updated The following works when message is not empty. What do I set or clear to render nothing when message is empty?
var store = {message: "hello cobber"}
Vue.directive('fill',
function (el, binding, vnode) {
if(binding.value)
el.innerHTML = binding.value
else
el = null
}
);
new Vue({
el: '#fill-example',
data: {
store: store
}
})
I'm one line away. Here's my fiddle. Anyone have any ideas?
It is possible to make a straightforward component to do what you want. A directive requires a bit more manipulation to be able to remove the element and put it back in the right place.
const vm = new Vue({
el: '#fill-example',
data: {
empty: '',
notEmpty: 'I have content'
},
components: {
renderMaybe: {
props: ['value'],
template: `<div v-if="value" class="boxy">{{value}}</div>`
}
},
directives: {
fill: {
bind(el, binding) {
Vue.nextTick(() => {
el.vFillMarkerNode = document.createComment('');
el.parentNode.insertBefore(el.vFillMarkerNode, el.nextSibling);
if (binding.value) {
el.textContent = binding.value;
} else {
el.parentNode.removeChild(el);
}
});
},
update(el, binding) {
if (binding.value) {
el.vFillMarkerNode.parentNode.insertBefore(el, el.vFillMarkerNode);
el.textContent = binding.value;
} else {
if (el.parentNode) {
el.parentNode.removeChild(el);
}
}
}
}
}
});
setTimeout(() => {
vm.empty = "Now I have content, too.";
}, 1500);
.boxy {
border: thin solid black;
padding: 1em;
}
<script src="//unpkg.com/vue#latest/dist/vue.js"></script>
<div id="fill-example">
Components:
<render-maybe :value="empty"></render-maybe>
<render-maybe :value="notEmpty"></render-maybe>
Directives:
<div class="boxy" v-fill="empty"></div>
<div class="boxy" v-fill="notEmpty"></div>
</div>