How can I add dynamically an attribute with VueJS? - javascript

I know I can set a value to an attribute dynamically with v-bind, however I would like add dynamically the attribute, not the value. Something like this (although this is not valid):
<a
:href="url"
{{ downloadable ? 'download' : null }}
class="link"
#click="onClick">
{{ text }}
</a>
Note: I'm not using JSX
I was thinking about using $attrs (https://v2.vuejs.org/v2/api/#vm-attrs) but it's read only.
Is there a way to do this on Vue?
Solution:
JavaScript:
new Vue({
el: '#app',
data() {
return {
msg: 'Inspect element to test',
downloadable: true
}
},
computed: {
dynamicAttribute() {
if(!this.downloadable) {
return null
}
return { [`download`]: "link or w/e" }
}
}
})
HTML:
<a v-bind="dynamicAttribute">{{msg}}</a>

If you want it to look like:
<a ... download="value">text</a>
with download visible only when downloadable is true, you can actually do it using v-bind:
https://jsfiddle.net/eywraw8t/274691/
You can check if it works by changing downloadable to true or false and inspecting the element.

Other than boolean attribute you cannot dynamically add or set attribute using Vue.js. For example -
v-bind:disabled="isActive"
If isActive is true, then the attribute disabled will be added to the element, otherwise it will be removed. This mechanism doesn't work for other attribute which are not boolean.
You can use Javascript for that purpose -
element.setAttribute('attributeName', 'value');

Related

How to add conditional styling in Vue.js using a method?

I want to add conditional styling in a child component based on the values of a prop passed from the parent component.
This a working example of conditional styling:
<li v-bind:class="[booleanValue ? 'stylingClassOne' : 'stylingClassTwo']"
but this is only applicable for when my styling is based on a single variable which can only be of two values (true/false).
I want to achieve conditional styling based on a variable that can take multiple values. Assume I pass a string from my parent component to my child component stylingDecider, which can be of values stylingClassOne, stylingClassTwo, stylingClassThree.
Therefore I want to do the following:
<li v-bind:class="getStylingClass(stylingDecider)"> but this does not work. The reason I need a method to decide what the styling is because there will be some other processing going on in the that will return a class based on said processing, so I can't just use <li v-bind:class="stylingDecider".
What am I doing wrong? Please advise, thanks.
I am using Vue 3 and bootstrap-vue 3.
I just created a working code snippet:
Vue.component('child', {
props: ['dynamicstyle'],
template: `<ul><li v-bind:class="getStylingClass(dynamicstyle)">Hello !!</li></ul>`,
methods: {
getStylingClass(stylingDecider) {
return stylingDecider;
}
}
});
var app = new Vue({
el: '#app',
data: {
stylingDecider: 'stylingClassTwo'
}
});
.stylingClassTwo {
background: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<child :dynamicstyle="stylingDecider">
</child>
</div>

Function Return HTML

I have function who return html
renderSuggestion(suggestion) {
const query = this.query;
if (suggestion.name === "hotels") {
const image = suggestion.item;
return this.$createElement('div', image.title);
} else {
let str = suggestion.item.name;
let substr = query;
return this.$createElement('div', str.replace(substr, `<b>${substr}</b>`));
}
},
But<b> element not render in browser as html element. Its display like string...
How I display this <b> element?
Tnx
That is because when you provide a string as the second argument of createElement, VueJS actually inserts the string as a text node (hence your HTML tags will appear as-is). What you want is actually to use a data object as the second argument, which give you finer control over the properties of the created element:
this.$createElement('div', {
domProps: {
innerHHTML: str.replace(substr, `<b>${substr}</b>`)
}
});
Of course, when you are using innerHTML, use it with caution and never insert user-provided HTML, to avoid XSS attacks.
You can also create a component and use v-html to render the output.
Declare props for your inputs:
export default {
props: {
suggestion: Object,
query: String
}
};
And use a template that uses your logic in the template part
<template>
<div class="hello">
<div v-if="suggestion.name === 'hotels'">{{suggestion.item.title}}</div>
<div v-else>
<div v-html="suggestion.item.name.replace(this.query, `<b>${this.query}</b>`)"/>
</div>
</div>
</template>
This allows for greater flexibility when using more complex layouts.
A working example here
Provide more detail(possibly a picture) of how it's not showing. Consider using a custom CSS class to see the div and what's happening to it.
bold {
border-style: black;
font-weight: bold;
}
then just use the "bold" class instead of "b".

vue.js put focus on input

HTML
<span :style="{ display : displayTitle }" #dblclick="showInput()">
{{ node.title }}
</span>
<input :style="{ display : displayTitleInput }" type="text"
#blur="hideInput1" #keydown="hideInput2"
#input="changeTitle(node.id , $event.target.value)"
:value="node.title">
JS
data() {
return {
displayTitle: "inline-block",
displayTitleInput: "none"
};
},
showInput() {
this.displayTitle = "none"
this.displayTitleInput = "inline-block"
},
hideInput1() {
this.displayTitle = "inline-block"
this.displayTitleInput = "none"
},
hideInput2(event) {
if (event.keyCode === 13) {
this.hideInput1()
}
},
I am a beginner Japanese web developer. I am not good at English, sorry.
HTML is in "v-for" (v-for="node in list").
When double click text, it turns to <input>.
I want to make it possible to focus on input when it appears.
I tried this but it didn't work.
HTML
<span :style="{ display : displayTitle }" #dblclick="showInput(node.id)">
{{ node.title }}
</span>
<input :ref='"input_" + node.id' :style="{display:displayTitleInput}" type="text"
#blur="hideInput1" #keydown="hideInput2"
#input="changeTitle(node.id , $event.target.value)"
:value="node.title">
JS
showInput(id) {
this.displayTitle = "none"
this.displayTitleInput = "inline-block"
this.$nextTick(this.$refs["input_" + id][0].focus())
},
There was no error on console, but didn't work.
Your primary problem is that $nextTick takes a callback function but you are executing
this.$refs["input_" + id][0].focus()
immediately. You could get your code working correctly with
this.$nextTick(() => {
this.$refs["input_" + id][0].focus()
})
However, I think you'll run in to further problems and your code can be made much simpler.
One problem you'll find is that all your node inputs will become visible when double-clicking on any of them due to your style rules.
You could instead store an "editing" flag somewhere either on the node or in a separate object.
Below is an example that simplifies your code by...
Using the array-like nature of ref when used within a v-for loop, and
Using the enter modifier on your #keydown event binding
new Vue({
el: '#app',
data: {
list: [
{id: 1, title: 'Node #1'},
{id: 2, title: 'Node #2'}
],
editing: {}
},
methods: {
showInput(id, index) {
this.$set(this.editing, id, true)
this.$nextTick(() => {
this.$refs.input[index].focus()
})
},
hideInput(id) {
this.editing[id] = false
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<ul id="app">
<li v-for="(node, index) in list">
<span v-show="!editing[node.id]" #dblclick="showInput(node.id, index)">
{{ node.title }}
</span>
<input v-show="editing[node.id]" type="text"
ref="input" :value="node.title"
#blur="hideInput(node.id)" #keydown.enter="hideInput(node.id)">
</li>
</ul>
The way you use this.$nextTick(); is incorrect. You should pass it a callback function.
this.$nextTick(function () {
this.$refs["input_" + id].focus()
})
https://jsfiddle.net/un65e9oc/7/
I'm not however sure how that array access is working for you, because as I notice, $refs is an object with the keys referring to the ref name.
[Edit: Thanks to #Phil's comment, above is clear.]
The above is the correct solution for your problem. Since you have already got that answer, I'll add something other than that.
The reason why you see this behavior is that because the reference you hold in $refs doesn't get updated when you change the visibility of the text box in your showInput() method. So when you call this.$refs["input_" + id].focus();, it's actually trying to set focus on a hidden element (because the current reference is not updated).
That's why you need to call the $nextTick() to update it. But if you wanted a quick fix to your problem, without calling $nextTick(), you could update it manually like this:
this.displayTitleInput = "inline-block"
this.$refs["input_" + id].style.display = this.displayTitleInput
this.$refs["input_" + id].focus();
This would also work :) Hope it helps!!
if you want to set focus after click on something and show input text box with set focus with vue js
directives: {
focus: {
// directive definition
inserted: function (el) {
el.focus()
}
}
}
and use custom directive for it. In case you need it should work on click then set with click
directives: {
click: {
// directive definition
inserted: function (el) {
el.focus()
}
}
}
and use it
<input v-focus> or <input v-click>
enter code here
The autofocus attribute is your friend:
<input type="text" autofocus />
It's work for me when we validate the form and want to set dynamically focus on each field
this.$validator.validateAll("FORM_NAME").then(valid => {
var errors = this.$validator.errors;
if (valid) {
console.log('All Fields are valid')
} else {
const errorFieldName = this.$validator.errors.items[0].field;
console.log(errorFieldName);
this.$refs[errorFieldName].focus();
}
});

Vue v-bind:class doesn't work on default truthiness check

So I have a simple v-bind:class like this: <div v-bind:class="{ showBranding: brandingEnabled }">BRANDING</div>
In my data, brandingEnabled is true and can be changed to false. Changing it to false does no remove the class.
It works perfect if I do this: <div v-bind:class="{ showBranding: (brandingEnabled == 'true') }">BRANDING</div>
Could this be an issue with my booleans being treated as strings? I have tried setting them (in my Vue data) to true, rather than "true" but that doesn't change anything either.
I have also tried setting the data to type: Boolean via props but to no avail.
I would really rather have it working with the simple syntax if possible...
Any help would be appreciated!
If showBranding is a CSS class, you have to add single quote around your css className, like this:
<div :class={'showBranding': brandingIsEnabled}>
// content
</div>
Then your class has to be inside a style tag into your component like this:
<style scoped>
.showBranding {
// content
}
</style>
Check also if your brandingIsEnabled data is inside your script tag like this:
<script>
export default {
data() {
return {
brandingIsEnabled: true
}
}
}
</script>
This example uses the single component syntax.
new Vue({
el: '#br',
data: {
showBranding: 'brandingClass',
brandingEnabled:true
}
});
CSS Code
.brandingClass{
display:none
}
html code
<div id="br">
<div :class="{ showBranding:brandingEnabled }">BRANDING</div>
</div>

vue.js computed beginner

I am trying to learn vue.js despite not having any background with javascript. I ran into some code when following a video that was teaching about 'computed', and I tried experimenting on it and had a bit of trouble along the way.
<div id='app'>
<p>Do you see me?</p>
<p v-if="show">Do you also see me?</p>
<button #click="showToggle1">Switch!</button>
</div>
new Vue({
el:'#app',
data:{
show = true;
},
computed:{
showToggle1:function(){
return this.show = !this.show
}
},
methods:{
showToggle2:function(){
this.show = !this.show;
}
});
Basically it's making "Do you also see me?" disappear and appear depending on the value of "show". I know that if you write #click:'showToggle2()' instead of #click:'showToggle1' at the button, the value changes and it works. I'm just having some trouble understanding how computed works and why showToggle1 doesn't change the value of show when I click the button
Some problems.
First, you have syntactical problems. data is an object, so instead of:
data:{
show = true;
}
Should be:
data:{
show: true
}
Next, computed properties are to be used like... properties. For example, like declared in data. So, typicall, you read from them. You don't execute computed properties in #click events. So this code:
<button #click="showToggle1">Switch!</button>
Is not correct. It will error because showToggle1 is not a method, it is, as said, a computed property. What you should have in click is a method, like:
<button #click="showToggle2">Switch!</button>
This will work because showToggle2 is a method. And you should use methods to perform changes.
Not, before going into the last and most tricky part, here's a working demo:
new Vue({
el: '#app',
data: {
show: true
},
computed: {
/*showToggle1: function() {
return this.show = !this.show;
}*/
},
methods: {
showToggle2: function() {
this.show = !this.show;
}
}
});
<script src="https://unpkg.com/vue"></script>
<div id='app'>
<p>Do you see me?</p>
<p v-if="show">Do you also see me?</p>
<hr>
Value of show: {{ show }}<br>
<button #click="showToggle2">Switch2!</button>
</div>
The tricky part is your computed property (which I commented out in the code above):
computed:{
showToggle1:function(){
return this.show = !this.show
}
},
Basically what it is doing is it is automatically negating the value of show whenever it changes.
This happens because the computed property is calculated whenever show updates. And what is happening is:
You initialize data with true (because of data: {show: true}).
The showToggle1 computed auto-recalculates, because it has this.show inside of it (it depends on it).
When recalculating, showToggle1 sets the value of show to false (because of return this.show = !this.show).
That's why show becomes false.
And that's also why whenever you change (even from the method, which is the correct place) the value of show to true it will automatically go back to false. Because any change in show triggers the showToggle1 computed recalculation, which sets show back to false.
In summary:
Use methods to perform changes.
Don't change properties inside computed properties.

Categories

Resources