Format of some words within {{$t('msg')}} - vue.js

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>

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

How to change the slider image dynamically

Thanks for browsing!
I am creating a slider with vue-image-lightbox.
However, now that I have a static src listed in the sample and the image is a slider, I would like to change this src to a dynamic one and display the image accordingly.
So I wrote the code as shown below, but the img src is set to undefind and the image is not displayed.
I thought I stored /uploads/1 here, but it is undefind.
So I'm hoping you can tell me how to store /uploads/1 in src and thumb.
I would like to ask for your wisdom.
<template>
<div>
<figure class="mr-3">
<a class="img-box" #click.prevent.stop="show(0)">
<img
:src="post.main_image_url"
:alt="post.main_image_url"
>
</a>
</figure>
<ImageLightBox :media="media" ref="lightbox" :show-light-box="false" :show-caption="true"></ImageLightBox>
</div>
</template>
<script>
import ImageLightBox from 'vue-image-lightbox';
require("vue-image-lightbox/dist/vue-image-lightbox.min.css");
export default {
name: 'lightbox',
components: {
ImageLightBox,
},
data() {
return {
post: {},
post_main_image_url: '',
media: [
{
// src: "/uploads/1" will work.
thumb: this.post_main_image_url,
src: this.post_main_image_url,
title: '朝日が昇る摩周湖(北海道川上郡弟子屈町)',
},
],
}
},
methods: {
show: function(index){
this.$refs.lightbox.showImage(index)
},
// data acquisition process
getData() {
this.$loading.load(
this.$auth.api.get(
'posts/' + this.post_id, {
params: {}
}
).then(res => {
this.post = res.data.post;
let mainImageUrl = res.data.post.main_image_url;
console.log(mainImageUrl)
// =>/uploads/1
let stringMainImageUrl = String(mainImageUrl)
console.log(stringMainImageUrl)
// =>/uploads/1
this.post_main_image_url = stringMainImageUrl
console.log(String(this.post_main_image_url))
// =>/uploads/1
}).catch(err => {
this.$errorHandlers.initial(err);
})
)
}
}
created() {
this.getData();
},
}
</script>
Hey just looked at your code, here are a couple of things:
// I couldn't find `show` method, is it declared?
<a class="img-box" #click.prevent.stop="show(0)">
<!-- You are initializing 'post' as empty string and I don't see a mounted hook
for initializing this property 'main_image_url' -->
<img :src="post.main_image_url" :alt="post.main_image_url" />
data() {
return {
post: {},
post_main_image_url: '',
media: [
{
/* When the component is mounted both properties will get initialized with an empty string.
You might need to initialize properties within the "mounted" hook. */
thumb: this.post_main_image_url,
src: this.post_main_image_url,
// I don't see this method being called anywhere in the code
getData() {
this.$loading.load(
this.$auth.api
.get('posts/' + this.post_id, {
params: {}
})

How to detect if text contain herf link

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>

VueJS: how to trigger 'change' on <input> changed programmatically

I'm going to build a customized virtual keyboard, so that's the first problem I've encountered.
I have an input element, whose value is changed from outside, in my case by pressing a button. The problem is that there seems to be no way to trigger the normal 'change' event.
Neither clicking outside the input, nor pressing Enter gives any result. What might be the correct way of solving this problem?
<template>
<div class="app-input">
<input #change="onChange" type="text" v-model="value" ref="input">
<button #click="onClick">A</button>
</div>
</template>
<script>
export default {
name: "AppInput",
data() {
return {
inputDiv: null,
value: ""
};
},
props: {
msg: String
},
methods: {
onClick() {
this.value = this.value + "A";
this.inputDiv.focus();
},
onChange() {
alert("changed");
}
},
mounted() {
this.$nextTick(() => {
this.inputDiv = this.$refs.input;
});
}
};
</script>
The whole pen can be found here.
v-on:change would only trigger on a direct change on the input element from a user action.
What you are looking for is a wathcer for your data property, whenever your value changes, watcher will execute your desired function or task.
watch: {
value: function() {
this.onChange();
}
}
The watch syntax is elaborated on the provided official vuejs docs link. use your data property as the key and provide a function as a value.
Check the snippet.
export default {
name: "AppInput",
data() {
return {
inputDiv: null,
value: ""
};
},
props: {
msg: String
},
methods: {
onClick() {
this.value = this.value + "A";
this.inputDiv.focus();
},
onChange() {
alert("changed");
}
},
// this one:
watch: {
value: function() {
this.onChange();
}
},
// --- rest of your code;
mounted() {
this.$nextTick(() => {
this.inputDiv = this.$refs.input;
});
}
};
When I build any new vue application, I like to use these events for a search input or for other inputs where I don't want to fire any functions on #change
<div class="class">
<input v-model="searchText" #keyup.esc="clearAll()" #keyup.enter="getData()" autofocus type="text" placeholder="Start Typing ..."/>
<button #click="getData()"><i class="fas fa-search fa-lg"></i></button>
</div>
These will provide a better user experience in my opinion.

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.