Nuxt.js: Can I add a class using a ternary operator based on a Boolean from a prop? - vue.js

I'm trying to use a Boolean in a prop to add a class based on a ternary operator. I'm obviously not doing it right because it is always evaluating to false.
<div :class="$style.inner + ' ' + (isRight == true ? 'is-right' : 'is-false')" :style="`color: #` + fontColor"></div>
If it's true, I need is-right added to the class, otherwise, this doesn't need to be added.
props: {
isRight: {
type: Boolean,
default: false
},
}
index.vue
<Signpost isRight=true/>
.is-right { padding-left:20% }
Looking at their Docs, I'm not sure I can do this with a Boolean actually.

To pass a Boolean to a prop you need to use v-bind or : like this:
<signpost v-bind:isRight="true" />
or
<signpost :isRight="true" />
Also if the only possible values for isRight is true or false you can shorten your ternary to just be:
:class="isRight ? 'is-right' : 'is-false'

I actually had a little syntax error for my prop that needed binding:
<Signpost :is-right="true"/>
And then on the div as #kissu said:
<div :class="[isRight ? 'is-right' : '']"></div>

Try passing the prop as :is-right="false" to have it passed down as Boolean and not as a basic String (is-right="false").
For the class, it can be written like as shown in the docs
<div :class="[isRight ? 'is-right' : 'is-false']"></div>
That way, you will even be able to do a strong equality check: isRight === true (comparing boolean to boolean).
VueJS devtools can help you see the type (thanks to the color).
There are some strongly recommended prop name casing recommendation that can be found here: https://v2.vuejs.org/v2/style-guide/#Prop-name-casing-strongly-recommended
props: {
greetingText: String // camel-cased here
}
<WelcomeMessage greeting-text="hi"/> <!-- kebab-cased here -->
Also, feel free to write ES6 template litterals, it may help the lecture. A working solution would be:
<div :style="`color: #${fontColor}`"></div>
Can also be done for your class, but I didn't want to risk a bad interpolation here.

Related

Can I pass a computed property and an object inside a v-bind:class in Vuejs?

I want to understand if I can do something like this, because I am trying but only getting erros... Forgive my bad english.
HTML file would be something like
<p :class="{mycss: isActive}, myComputedProperty" > My text </p>
and the component file would have something like
export default {
data () {
return {
isActive: true
}
},
computed: {
myComputedProperty () {
// do something
}
}
}
class value is an expression. If it doesn't make sense in raw JavaScript, it doesn't make sense there. Here comma operator is used, so the expression evaluates to myComputedProperty, and {mycss: isActive} part is discarded.
The format for combined class value is documented:
:class="[{mycss: isActive}, myComputedProperty]"
Since computed values are involved, defining the whole class object as a computed will result in cleaner template code.
I think the error is in your HTML - the comma is probably the cause. There are lots of ways to format strings, but this is one option:
<p :class="isActive ? 'mycss ' + myComputedProperty : myComputedProperty" > My text </p>

vue does not recover from me specifying a non existing location for v-model

When I have a textarea like
<textarea v-model="foo.abc.text"></textarea>
and either foo or foo.abc does not exist yet then
vue removes either parts of the DOM or is giving me a blank page.
It does never recover.
That alone is annoying, regardless of if I am using a debug version of vue or not.
If I try to use an approach that I have been advised to use earlier like
<textarea v-model="foo?.abc?.text"></textarea>
then I am still out of luck, I presume that I get a "rvalue" using those question marks and what I need rather is a variable location.
How do I, with as little trickery as possible, allow v-model to exist later on even if it doesnt exist now (late binding)?
Just shape your data accordingly and initialize it with empty values:
data(){
return {
foo: {
abc: {
text: ''
}
}
}
}
You can later populate it e.g. with the result of api call, but it's still better to initialize data properly
I would suggest going the :value + #input way. It allow more control over the input model, and does not require hiding it.
<textarea :value="!!foo && foo.abc.text" #input="(val) => !!foo && (foo.abc.text = val)" />
You can even hook in a validator:
<textarea
:value="!!foo && foo.abc.text"
#input="(val) => !!foo && (foo.abc.text = val)"
:rules="v => !v && 'The object has not been initialised'"
/>
I found a solution I can live with and then I got a comment in the same direction:
Conditionally showing the textarea.
v-if seems to do it but it falls under the "trickery" category, I think (angularjs would be more relaxed).
<textarea v-if="foo!=null" v-model="foo.abc"></textarea>
The symptom to hiding components if something is not all correct is not the best part of vue.js. Better show them and color them red.

Avoid converting null to empty string on b-input blur

I'm facing an issue with reactivity in Vue JS 2.6.6. Not sure if it's an issue at all or I'm missing something here.
I'm hydrating an object from MySQL DB through axios getting:
resource:Object
id: 123
alt: null
file:"2a72d890d0b96ef9b758784def23faa1.jpg"
type:"jpg"
}
The form is handling the properties via v-model:
<b-input v-model="resource.alt" />
What I can see is that on blur event, besides no changes were made, the resource.alt property is being changed from null to an empty string ""
resource:Object
id: 123
alt: ""
file:"2a72d890d0b96ef9b758784def23faa1.jpg"
type:"jpg"
}
So what could be the best way to handle this? I don't think that fetching all null fields from DB as empty strings is a nice solution. I mean something like IFNULL(files.alt,'') AS alt
On the other hand, since I fetch a lot of records by doing a loop through them all and setting all null properties to empty string looks very inefficient.
What I need is to keep null properties as null or parse in some way all null properties to empty string from the beginning.
FYI, I'm using vee-validate, so I need to validate if the object has changed with pristine flag which is set to false if the object has been manipulated. This behavior is setting the flag to false because of the change from null to ""
EDIT
This is getting odd. The way I propagate the resource to local data in the component is by clicking an element from a list.
data: () => ({
files: [],
resource: {}
})
<div v-for="file in files" :key="file.id"
#click.exact="handleIndividualCheck(file)">
{{ file.file }}
</div>
methods: {
handleIndividualCheck (item) {
this.resource = { ...item }
}
}
The first time I select an item, the null property is converted to an empty string as explained.
The next times, everything works as expected
This is in fact a bootstrap-vue bug for version 2.19.0
I've reported the issue and fixes will be merged in the next release.
Link to issue: https://github.com/bootstrap-vue/bootstrap-vue/issues/6078

Vue string concatenation with values and conditional

Is there an option to display or hide a string based on a truthy value and when falsy it displays none, or can we only use ternary operator for this.
I know it is possible to do with class binding, where you add a value and then add the condition:
:class="{ 'class-only-if-true': false }"
so my question would be if it is possible to do the following only as a string:
{{'only display if true' : true}}
and possibly concatenate with data as such:
{{'only '+data+' if true' : true}}
While I know it would be possible adding a wrapper tag around this and adding the condition in here, but in this case I can't use any <div>, <span> or other.
While I could make it work as such (ternary operator):
{{ true ? 'only '+data+' if true' : ''}}
I was wondering if there would be an approach I have overlooked similar to the class binding condition.
Hope this makes sense.
Instead of the ternary operator {{ true ? 'only '+data+' if true' : ''}}, you could use the && operator like {{ true && 'only '+data+' if true'}}. Of course don't use data as it points to the components whole data object.

How to specify multiple dynamic attributes by single computed prop in VueJS

I have this html element:
Link text
I want to add data-tooltip and title attributes dynamically by condition:
Link text
Is there any way in VueJS to add multiple dynamic attributes at same time:
<!-- instead of this: -->
Link text
<!-- something like this: -->
<a href="javascript:" ...tooltipAttributes >Link text</a>
You could take advantage of v-bind on the DOM element you wish to apply multiple attributes to based on some dynamically changing condition.
Here's a Plunker example demonstrating how you might go about it.
Take note of the object returned:
computed: {
multiAttrs() {
return this.showAttrs ? {
'data-toggle': 'tooltip',
title: 'Some tooltip text',
} : null;
}
}
You should be able to use v-bind="tooltipAttributes"
the docs here https://v2.vuejs.org/v2/api/#v-bind have more info, but the key part is under usage
Dynamically bind one or more attributes, or a component prop to an expression.
From the Docs:
1. You can dynamically bind multiple attributes/props to a single element by using v-bind:
(no colon, no extra attribute, just v-bind)
<a href="#" v-bind="tooltipAttributes" >Link text</a>
2. And then declare the variable in the computed section:
(you can also declare it in the data section, but that would require manual direct value changes)
computed() {
return {
tooltipAttributes: {
title: 'Title',
'data-toggle': this.toggle === true && !disabled
}
}
}
Note: Attributes with dashes/hyphens - in them (e.g. data-toggle) need to be a string because Javascript doesn't recognize - as a valid symbol in variable naming.
This is THE SAME AS:
<a href="#" title="Title" :data-toggle="this.toggle === true && !disabled" >Link text</a>