Vue 3s' i18n plugin has build-in modifiers for (linked) messages.
message: {
edit: 'edit',
editDesc: '#. capitalize:edit description'
}
which works fine, but the problem is I couldn't find a way to use those modifiers from within a template.
For example if I want an edit button with an capitalized 'e': <button>Edit</button>
<button>
{{ $t("edit") }}
</button>
i tried several things:
<button>
{{ $t("edit.capitalize") }}
</button>
<button>
{{ $t("capitalize.edit") }}
</button>
<button>
{{ $t(":capitalize.edit") }}
</button>
but none of these worked.
Is it even possible to use those modifiers in a template/from code, or to I have to use plain js for it.
If it is possible, how?
The modifiers can only used in i18n config messages, I suggest to define the message with capitalized format and lower it when it used in the middle/end of sentence :
message: {
edit: 'Edit',
editDesc: '#:message.edit description',
editApprove: 'Please approve the #.lower:message.edit'
}
I found it easiest to modify this in the template using plain JavaScript String methods. It has been more useful in my experience to store the message strings capitalized, then use the toLowerCase method.
<button>
{{ $t("edit").toLocaleLowerCase() }}
</button>
I have the same issue. As a workaround I use temporary linked message:
message: {
tmp: 'Actual text',
content: "#.lower:tmp"
}
And then in the template:
<p>{{ $t('message.content') }}</p>
It's ugly tho...
Related
I have a paragraph in my app, in which the user can translate it at the click of a button. I interpolate the text seen via my props (props.row.text) and my data (this.data.translatedText).
The data property is empty/null until the button is cliked, which therefore causes me to receive the error "data is null" in my Console. I attempt to fix this with the code below, however, this throws me error but does not display the interpolated text after the button click.
Does anyone have a suggestion for how best to structure this, so that the {{ props.row.text }} is replaced by {{ this.data.translatedText }} after the button is clicked?
Component.vue
<span id="translationButton">
<translation-button #click="calltranslatedText()" #changeTitle="ChangeT" />
</span>
<div class="ticket-text-container">{{ props.row.text }}</div>
<div class="ticket-text-container" v-if="calltranslatedText()">{{ this.data.translatedText }}</div>
methods: {
ChangeT(title)
{
this.translatedText = title
},
calltranslatedText() {
return true
},
Firstly don't use this and data in the template just indicate a prop name you defined in data function.
Secondly, you can just use translatedText in v-if to show div with the translated text.
<div class="ticket-text-container" v-if="translatedText">{{ translatedText }}</div>
Don't forget to set translatedText to an empty string or null to hide this block again if needed.
I have a GenericButton component that I need to pass a couple of props into. One is for the class it will use and another is a class that will apply if the button is disabled. I read the Vue docs here: https://v2.vuejs.org/v2/guide/class-and-style.html, but I am still having issues. in the docs, they say you can do this to bind a class to a JS expression, and even bind multiple classes.
<div
class="static"
v-bind:class="{ active: isActive, 'text-danger': hasError }"
></div>
I pass a prop called injectedClass and one called isDisabled. This is my button:
<button
:disabled="isDisabled"
:class="{ injectedClass, disabled: isDisabled }"
#click.stop="handleClick"
>
{{ text }}
</button>
The disabled class applies how I would expect, but my passed injectedClass isn't applied. The injectedClass is applied if I do this:
<button
:disabled="isDisabled"
:class="injectedClass"
#click.stop="handleClick"
>
{{ text }}
</button>
It isn't even working if I do this:
:class="(injectedClass, { disabled: isDisabled })"
My question is how can I apply the class I pass through to my component and also conditionally apply my disabled class?
Just use an array syntax
:class="[injectedClass, { disabled: isDisabled }]"
I know this is a common question, but I have been going through my files now so many times without being able to the locate the error.
I am getting this error when I try to route to my components in my navigation menu.
My app.vue file:
<template>
<div id="app">
<Navbar
:nav-links="navLinks"
/>
<router-view/>
</div>
</template>
<script>
import Navbar from '#/components/Navbar'
export default {
components: {
Navbar
},
data: () => ({
navLinks: [
{
text: 'Home',
path: '/home'
},
{
text: 'About',
path: '/about'
},
{
text: 'Contact',
path: '/contact'
}
]
})
}
</script>
My Navbar component (This is where the error happens)
<template>
<nav>
<ul>
<li v-for="{link, index} in navLinks" :key="index"
#mouseover="hover = true"
#mouseleave="hover = false">
<router-link :to="link.path">
{{ link.text }}
</router-link>
</li>
</ul>
</nav>
</template>
<script>
export default {
props: ['navLinks'],
data(){
return {
hover: false,
}
}
}
</script>
How do I fix this?
<li v-for="{link, index} in navLinks" :key="index"...
should be
<li v-for="(link, index) in navLinks" :key="index"...
As it's now (destructured), link refers to a link property inside the object, not the object itself. Additionally, index is probably undefined, since the navLinks objects probably don't have an explicit property index. Therefore Vue might also complain about using invalid indexes in v-for.
Since you're only using the path prop, you could actually use destructuring, like this:
<li v-for="({ path }, index) in navLinks" :key="index"
#mouseover="hover = true"
#mouseleave="hover = false">
<router-link :to="path">
</li>
Another, unrelated note: hover property is currently being shared across all navLinks. If you expect it to somehow be related to the currently hovered element, yo uhave to save that separately (probably inside the navLink itself).
As for :nav-links="navLinks", what you've done is not only perfectly legal, but the recommended way of doing it (it's according to the HTML spec). Using :navLinks="navLinks" relies on Vue's HTML parser, which converts it to nav-links behind the scenes - inspect the HTML element and you'll notice it).
If you want to get into the details, you could have a look at this discussion on the subject. The result was: use either, but if you use camelCase it will be inconsistent with the rendered markup. If you use kebab-case, it will be consistent with rendered markup, so you won't have to deal with this difference when writing tests, for example, should you ever need to select elements by their attributes (jest converts camelCase to lowercase - hence it's inconsistent with the rendered markup, so the tests start passing/failing based on whether mount or shallowMount is used. Goes without saying, that's not really a good testing setup. )
The same exact discussion goes for using <SomeComponent /> vs <some-component />. While both work, using first needs to be addressed when writing tests if you need to select stubbed subcomponents.
Besides, vue/attribute-hyphenation (the way you did it) is part of the following vue linting presets:
plugin:vue/strongly-recommended
plugin:vue/vue3-recommended
plugin:vue/recommended
A prop in the Navbar component is named navLinks but you access it outside as nav-links.
This should work:
:navLinks="navLinks"
Incorrect syntax for v-for with {}. Use ():
li v-for="(link, index) in navLinks
You have done two mistakes here.
one is:
<template>
<div id="app">
<Navbar
:nav-links="navLinks"
/>
<router-view/>
</div>
Here you are binding with different name(nav-links), you should keep same name with which you are binding data and the name inside the props(navLinks).
Both names should be same.
Second one:
v-for="{link, index} in navLinks"
The syntax is wrong, the correct syntax should be
v-for="(link, index) in navLinks"
I'm making a link/button component which either can have a button or an anchor wrapper, a text and an optional icon. My template code below is currently rendering either an anchor or a button (with the exact same content) based on an if statement on the wrapper element, resulting in duplicate code.
<template>
<a v-if="link" v-bind:href="url" class="btn" :class="modifier" :id="id" role="button" :disabled="disabled">
{{buttonText}}
<svg class="icon" v-if="icon" :class="iconModifier">
<use v-bind="{ 'xlink:href':'#sprite-' + icon }"></use>
</svg>
</a>
<button v-else type="button" class="btn" :class="modifier" :id="id" :disabled="disabled">
{{buttonText}}
<svg class="icon" v-if="icon" :class="iconModifier">
<use v-bind="{ 'xlink:href':'#sprite-' + icon }"></use>
</svg>
</button>
</template>
Is there a more clean way for wrapping my buttonText and icon inside either an anchor or button?
I've solved my issue by intensive Google-ing! Found this issue regarding Vue on Github which pointed me in the right direction.
Small piece of backstory
I'm using Vue in combination with Storybook to build a component library in which a button can either be a button or an anchor. All buttons look alike (apart from color) and can be used for submitting or linking. To keep my folder structure ordered, I would like a solution that generates a multiple buttons types (with or without link) from one single file.
Solution
Using computed properties I'm able to "calculate" the necessary tag, based on the url property of my component. When a url is passed, I know that my button has to link to another page. If there is no url property it should submit something or preform a custom click handler (not in the sample code below).
I've created the returnComponentTag computed property to avoid placing any complex or bulky logic (like my original solution) in my template. This returns either an a or a button tag based on the existence of the url property.
Next, as suggested by ajobi, using the :is attribute I'm able to define the component tag based on the result of my computed property. Below a stripped sample of my final (and working) solution:
<template>
<component :is="returnComponentTag" v-bind:href="url ? url : ''" class="btn" :class="modifier" :id="id">
{{buttonText}}
</component>
</template>
<script>
export default {
name: "Button",
props: {
id: {
type: Number
},
buttonText: {
type: String,
required: true,
default: "Button"
},
modifier: {
type: String,
default: "btn-cta-01"
},
url: {
type: String,
default: ""
}
},
computed: {
returnComponentTag() {
return this.url ? "a" : "button"
}
}
};
</script>
You could extract the wrapping element into a dedicated component.
<template>
<a v-if="link" v-bind:href="url" class="btn" :class="modifier" :id="id" role="button" :disabled="disabled">
<slot></slot>
</a>
<button v-else type="button" class="btn" :class="modifier" :id="id" :disabled="disabled">
<slot></slot>
</button>
</template>
// You would use it like this
<SomeComponent /* your props here */ >
{{buttonText}}
<svg class="icon" v-if="icon" :class="iconModifier">
<use v-bind="{ 'xlink:href':'#sprite-' + icon }"></use>
</svg>
</SomeComponent>
There are multiple ways of doing this. Two examples would be the following based on the point of view:
You are defining two different components (Button or Anchor) and want to use a wrapper to render either one of them.
You could seperate the Wrapper Content into two components so that the wrapper only decides on which of the components to render (either the Button or the Anchor).
The problem with this approach could be you will have doubled code for methods and styling for the button and anchor component.
You are defining the content as a component and use the wrapper to define what to wrap the content in.
See Answer of https://stackoverflow.com/a/60052780/11930769
It would be great to know, why you would want to achive this. Maybe there are better solutions for your usecase. Cheers!
This question already has answers here:
VueJS conditionally add an attribute for an element
(11 answers)
Closed 4 years ago.
I'd like to learn what is the best way to conditionally render an HTML attribute in Vue.js. For example, add data-toggle="tooltip" if there is a tooltip message for current instance.
The code I have now:
<span
:data-toggle="!!col.col_spec.tooltip ? 'tooltip' : ''"
:title="col.col_spec.tooltip"
>
{{ col.col_spec.title }}
</span>
Though, I don't like the 2nd line much… Even if I use computed property here, I'd prefer not to have data-toggle attribute at all, when there is no tooltip to display.
Very so elegant solution:
<span
:data-toggle="!!col.col_spec.tooltip ? 'tooltip' : false"
:title="col.col_spec.tooltip"
>
{{ col.col_spec.title }}
</span>
Yes, yes, yes, it's just necessary that there is not an empty string, but a Boolean false
Something like:
<span ref="column">
{{ col.col_spec.title }}
</span>
And in Vue:
mounted(){
if (this.col.col_spec.tooltip){
this.$refs.column.setAttribute("data-toggle", this.col.col_spec.tooltip);
}
}
A bit late, but here is my take on it:
HTML:
<span
:data-toggle="tooltip"
:data-original-title="tooltipTitle"
>
{{ tooltipTitle }}
</span>
Vue:
methods: {
tooltipTitle: function() {
var title = this.col.col_spec.title;
if (!!title) return title;
return false;
}
}
This will remove the "data-original-title" attribute if there is none to display, consequently removing the tooltip altogether. You must use "data-original-title" instead of just "title" because Bootstrap will automatically add it once you initialise the "title" attribute, and changing "title" to false will not remove the "data-original-title".
Here's another working but not so elegant solution:
<span v-if="!!col.col_spec.tooltip" data-toggle="tooltip" >
{{ col.col_spec.title }}
</span>
<span v-else >
{{ col.col_spec.title }}
</span>