return elements from a vuejs method - vuejs2

I'm a bit new to vuejs and I'm not even sure what exactly am I looking for,
I have this template:
<template>
<md-content class="md-elevation-2">
<div class="md-layout">
<div class="md-layout-item" v-for="key in ruleData">
{{ getKeyOutput(key) }}
</div>
</div>
</md-content>
</template>
and my script is:
<script>
export default {
props: ['ruleData'],
methods: {
getKeyOutput(value) {
switch (typeof value) {
case 'string':
if (/(ban)$/g.test(value)) {
return createElement(`<h1>${ value }</h1>`) // here is the problem
} else {
return value
}
break
case 'number':
return String(value)
break
case 'boolean':
return String(value)
break
default:
return value
break
}
}
}
}
</script>
What I want to do is on some case return the string, and in some other cases like return an HTML component like h1 for example, and I can't seem to understand how I need to do this, or even if I have the correct approach for this.

You have to use v-html directive to render html tags that is stored as a string.
if you don't use v-html then vuejs by default escapes the html tags and therefore the html tags are displayed as a plain text. You don't need to use createElement() at anyplace in your code, just remove it.
Change your vue template code as below and verify if you are getting the expected result
<div
class="md-layout-item"
v-for="(value,key) in ruleData"
:key="key"
v-html="getKeyOutput(value)">
</div>
You don't need to use createElement() anymore, just return the html code as a string or template string
if (/(ban)$/g.test(value)) {
return `<h1>${ value }</h1>`; //problem solved
} else {
return value
}
Read More details about v-html in the docs https://v2.vuejs.org/v2/guide/syntax.html#Raw-HTML

Related

What is the kebab case for a forward slash to use with Vue?

Keycodes are deprecated and we can no longer use #keyup.keyCode.191
I tried using
#keyup.slash
#keyup.divide
All possible values from KeyboardEvent/key/Key_Values
I don't think you can do this directly. From stepping through the code it seems to be comparing the modifier to the event.key, which would be '/' in this case. You can't write #keyup./ as that isn't considered valid.
You could achieve the same effect by performing the key test yourself:
Vue.createApp({
methods: {
onSlash (ev) {
if (ev.key !== '/') {
return
}
console.log('/ pressed')
}
}
}).mount('#app')
<script src="https://unpkg.com/vue#3.0.2/dist/vue.global.js"></script>
<div id="app">
<input #keyup="onSlash">
</div>

How use v-if with array length

I want to show a div if the array length is more than 0. I used it below.
HTML:
<div v-if="countfunc > 0">
<div v-for="nhp in countfunc">
<p v-text="nhp.code"></p>
</div>
</div>
Vue:
computed: {
countfunc: function(){
//this return a js array
}
}
How can I solve my problem?
You can use it as countfunc.length>0
The thing is, if countfunc is an empty array it will not show anything anyway, so this double check is a little bit unnecessary. Of course in your code you're forgetting the :key property in v-for as well.
Here you can try on codepen, try to delete the object inside the array and let it empty like
<script>
export default {
data() {
return {
items: [],
};
},
};
</script>
And you can see that not will be rendered since the array is empty.

Vue.js this.$refs empty due to v-if

I have a simple Vue component that displays an address, but converts into a form to edit the address if the user clicks a button. The address field is an autocomplete using Google Maps API. Because the field is hidden (actually nonexistent) half the time, I have to re-instantiate the autocomplete each time the field is shown.
<template>
<div>
<div v-if="editing">
<div><input ref="autocomplete" v-model="address"></div>
<button #click="save">Save</button>
</div>
<div v-else>
<p>{{ address }}</p>
<button #click="edit">Edit</button>
</div>
</div>
</template>
<script>
export default {
data() {
editing: false,
address: ""
},
methods: {
edit() {
this.editing = true;
this.initAutocomplete();
},
save() {
this.editing = false;
}
initAutocomplete() {
this.autocomplete = new google.maps.places.Autocomplete(this.$refs.autocomplete, {});
}
},
mounted() {
this.initAutocomplete();
}
}
I was getting errors that the autocomplete reference was not a valid HTMLInputElement, and when I did console.log(this.$refs) it only produced {} even though the input field was clearly present on screen. I then realized it was trying to reference a nonexistent field, so I then tried to confine the autocomplete init to only when the input field should be visible via v-if. Even with this, initAutocomplete() is still giving errors trying to reference a nonexistent field.
How can I ensure that the reference exists first?
Maybe a solution would be to use $nextTick which will wait for your DOM to rerender.
So your code would look like :
edit() {
this.editing = true;
this.$nextTick(() => { this.initAutocomplete(); });
},
Moreover if you try to use your this.initAutocomplete(); during mounting it cannot work since the $refs.autocomplete is not existing yet but I'm not sure you need it since your v-model is already empty.
I think it's because your "refs" is plural
<input refs="autocomplete" v-model="address">
It should be:
<input ref="autocomplete" v-model="address">

vue: escape and render HTML string?

I'm trying to render some HTML string in template, but I want them to be string, I don't want to render "rich text"
I started with:
<template>
<div>{{'something <like /> this'}}</div>
</template>
but vue will render the code above into follwing:
<like /> will be rendered as html tag
<div>{{'something <like></like> this'}}</div>
but what I want is this:
<div>something <like/> this</div>
I know I can put the text inside data
<template>
<div>{{text}}</div>
</template>
<script>
export default {
data() {
return {
text: 'something <like /> this'
}
}
}
</script>
but this would become tedious, and how can I do it directly inside template
I think you need to pre-process your string first like this
text.replace(/&/g, "&").replace(/>/g, ">").replace(/</g, "<").replace(/"/g, """);
Then you can use it in the template
You'll need to replace the characters < and > as you've pointed out, but you'll also need to use the v-html directive since the tags in the string literal break the mustache binding.
In your script:
methods: {
htmlToText(html) {
return html.replace(/</g, '<').replace(/>/g, '>')
},
}
In your template:
<div v-html="htmlToText('something <like/> this')"></div>
Doing <div>{{ htmlToText('something <like/> this') }}</div> will not work.

Custom directive v-focus is not working on vuetify component

I'm trying to use a vuejs custom directive called focus on a component from vuetify which is v-field-text.
directives: {
focus: {
// directive definition
inserted: function(el) {
el.focus();
}
}
}
I have a todo list, and my todos are printed with v-for, I also have an option to edit todos, whenever i click on edit button todo dispears and todo edit input apears.
I am using this focus directive to auto focusing the input.
However when i use this like this is not working:
<v-field-text v-focus></v-field-text>
But it works like this:
<input v-focus />
When i console.log the el from the directive, i see that its referring to a div element created by vuetify.
How to fix this issue?
The reason you're seeing a div when using v-focus on those elements is because they are being wrapped in a div. To get around this with third party components you don't control the code to, you may use something like the following function:
import Vue from 'vue'
Vue.directive('focus', {
inserted: function(el) {
// Recursion based function for finding an input
// nested within other elements.
let findInput = (el, max_depth = 5) => {
// We found the input, so we return it, which causes
// the entire function stack to pop
if (el.nodeName === 'INPUT') {
return el
}
// Prevent infinite recursion by providing a maximum
// depth, and returning when we've reached that depth
if (max_depth === 0) {
return null
}
// Our current element is not an input, so we need to loop
// over its children and call findInput recursively
for (let child of el.children) {
let input = findInput(child, max_depth - 1)
// We've found our input, return it to unwind the stack
// otherwise, continue through the loop
if (input) {
return input
}
}
// Fallback in case for when el has no children, or we reached the end of the loop with no input
return null
}
// Start searching for the input. We can optionally
// pass a higher max_depth. Use with caution.
let input = findInput(el, 20)
if (input) {
input.focus()
}
}
})
This is using recursion to step through each elements children, searching for an element with nodeName === 'INPUT'.
As an example, the following complex structure would be parsed and the first input found would be focused:
<div v-focus>
<div>
<div>
<div>
<div>
<div>
<div>
<div>
Hello
</div>
<p>
world
</p>
<span>!</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div>
<div>
<div>
<input type="text" value="I will be focused">
</div>
</div>
</div>
</div>
Please try this solution. It's working for me:
directives: {
focus: {
// directive definition
inserted: function (el) {
let childData = el.querySelectorAll("input")[0];
childData.focus()
}
}
}