Reusing small bits of markup inside a component in a vue file - javascript

I am just starting to learn Vue and while doing a very basic template I found myself repeating small bits of HTML / components in the same template because of a v-if and different containing components depending on its result.
Though I have solved this repeating the same lines a few times I find it less than ideal and would like to know if there is a better way to make it DRY (without having to create a new component).
I would like to be able to reuse the following lines:
{{ item.name }}
And a bit more of markup, but not much and no "functionality"
Definitively creating a new vue file with a new component would be overkill. But having to repeat those lines (the real ones a little bit more complex) really gives me pain a little bit.
Here is a very simple example that illustrates the problem.
Please remember that this is not the real code, the real code is inside a vue file (but I do not know how to simulate this here or in codepen)!!
var app = new Vue({
el: '#app',
data: {
items: [
{
name: 'name 1',
link: 'http://www.google.com'
},
{
name: 'name 2',
link: ''
},
]
},
methods: {
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.1/vue.min.js"></script>
<div id="app">
<template v-for="item in items">
<a :href="item.link" v-if="item.link != ''">
{{ item.name }}<br>
And a bit more of markup, but not much and no functionality
</a>
<p v-else>
{{ item.name }}<br>
And a bit more of markup, but not much and no functionality
</p>
</template>
</div>

You can use a component with "is" property set to the tag type you want rendered:
<div id="app">
<template v-for="item in items">
<component :href="item.link || false" :is="item.link ? 'a' : 'p'">
{{ item.name }}<br>
And a bit more of markup, but not much and no functionality
</component>
</template>
</div>
false on href causes that attr to not be rendered at all

Related

Make plural with nuxt/i18n using as component

I am using nuxt/i18n plugin for localize my app.
My translation json file like below,
{
"key": "a car | {count} cars"
}
I now if i wrote as this, i could
{{ $tc('path.to.key',2,{count: 2}) }}
But how can i use this tricky while I am using this translation as below.
<i18n path="path.to.key">
<template #count>
<!-- some html code -->
</template>
</i18n>
I am using this way because i need to use html code for styling to {count} variable.
How can i singular or plural this translation?
Is there any property for i18n component to use this?

[VuePress]: Extract links from data

In VuePress, I am modifying the default home page.
Given the data that I obtain from the markdown page, how can I do to interpret the links provided in that data?
I get the data in the following way:
./README.md
---
news: [{title: A title, content: my content}]
---
Then I use the data as follows:
./components/Home.vue
...
<ul>
<li v-for="(item, index) in data.news :key="index">
<b>{{ item.title }}</b>
{{ item.content }}
</li>
</ul>
...
<script>
export default {
computed: {
data() {
return this.$page.frontmatter;
},
}
</script>
When this is displayed, the content in news.content does not interpret the links neither in HTML, nor in Markdown. How can I do to be able to include links in that news.content?
PS: The work with news.content is actually more complex visually, this is why I need some way to pass content to my Home.vue which can interpret links.
The v-html directive allows rendering html from the content data.

Vue template isn't rendering in for loop

So after following a beginner Vue tutorial to setup a Todo app, I decided to try to adapt some parts of it for a website I'm trying to make. What I'm stuck on is that despite everything saying my for-loop is supposed to work, it doesn't.
The project itself was created using the vue-cli, and most of the code copy-pasted from the tutorial. (which is working fine with its own for-loop)
It seems like the data might be not passed onto the template maybe?
I have tried:
having the info inside the props and data sections
passing whole object and only parameters to the template
tried with hard-coded values inside array which is iterated on
(After setting up a new vue-cli project:)
App.vue:
<template>
<div id="app">
<create-section v-on:create-section="addSection" />
<section v-for="section in sections" v-bind:key="section.title" :info="section"></section>
</div>
</template>
<script>
import CreateSection from "./components/CreateSection";
import Section from "./components/Section";
export default {
name: "App",
components: {
CreateSection,
Section
},
data() {
return {
sections: []
};
},
methods: {
addSection(section) {
this.sections.push({
title: section.title,
description: section.description
});
console.log(
"Added to sections! : " + section.title + " | " + section.description
);
console.log("Sections length: " + this.sections.length);
}
}
};
</script>
Section.vue
<template>
<div class="ui centered card">
<div class="content">
<div class="header">{{ info.title }}</div>
<div>{{ info.description }}</div>
</div>
</div>
</template>
<script type = "text/javascript" >
export default {
props: {info: Object},
data() {
return {};
}
};
</script>
Expected result:
Display Section template on the website (after creating it with addSection that another script calls. Not included for brevity)
Actual result:
Nothing is displayed, only a empty tag is added
I believe the problem is that you've called it Section. As <section> is a standard HTML element you can't use it as a component name.
There is a warning built into the library but it seems to be case sensitive, which isn't entirely helpful. Try changing your components section to this:
components: {
CreateSection,
section: Section
},
You should then see the warning.
The fix would just be to call it something else.
This is mentioned in the first entry in the style guide:
https://v2.vuejs.org/v2/style-guide/#Multi-word-component-names-essential
section is an existing HTML5 element, you should name your section component something different.
If you really want to name the component Section, register it as 'v-section'
The problem is that when you do the loop in the <section v-for="section in sections" v-bind:key="section.title" :info="section"></section> the Array sections is not ready, there is nothing there.. so when you add new things to this array you need to trigger (computed prop) to send again the data to the section component.
Aside from the issue with using an existing HTML5 command as a name for your Vue component (you should change that to another name by the way), you should also look into how you declared the props within Section.vue. The code below shows the correct way to do it:
<script type = "text/javascript" >
export default {
props: ['info'],
data() {
return {};
}
};
</script>
The props take in the name of the property being declared from the parent component and should be a string.
Hope this helps.

How to insert a new tag between every two v-for produced tags?

Is there a way I can insert new tag between every two v-for produced tags?
Like Array.join().
Background:
I want to insert a <span>,</span> between every two <router-link></router-link>.
<router-link></router-link> is produced by v-for, the code like this:
<router-link tag="a"
v-for="text in ['vue', 'react', 'JQuery']"
:key="text"
>
{{ text }}
</router-link>
Run it and seems like this:
vue react JQuery
I don't know how to directly insert <span>,</span> between these so I move it into a <div>:
<div v-for="text in ['vue', 'react', 'JQuery']"
:key="text">
<router-link tag="a">
{{ text }}
</router-link>
<span>, </span>
</div>
Run it and seems like this:
vue, react, JQuery,
so the question is that the last ‘,’ is redundant.
I can fix it by v-show:
<div v-for="(text, index) in ['vue', 'react', 'JQuery']"
:key="text">
<router-link tag="a">
{{ text }}
</router-link>
<span v-show="index !== 2">, </span>
</div>
It works nice, but I want to know is there a easy way to do this?
Thanks response.
Your last approach is the way to go, but you should probably use Array.length for processing the last occurrence, in case the array changes.
<span v-show="index !== (items.length - 1)">, </span>
Or use plain CSS (notice I'm adding class route on the div element):
div.route:not(:last-child) > a::after {
content: ',';
text-decoration: none;
}
Another addition to what has already been said. Inverting the order makes it easier to avoid the unneded comma (you always skip the first one, instead of need to take care of the array length)
<div v-for="(text, index) in ['vue', 'react', 'JQuery']"
:key="text">
<span v-if="index !== 0">, </span>
<router-link tag="a">
{{ text }}
</router-link>
</div>
As an addition to what jom said: I'd rather use v-if than v-show, because as the docs say:
prefer v-show if you need to toggle something very often, and prefer v-if if the condition is unlikely to change at runtime.
Since you do not want to toggle the comma at any point, just don't render it at all :)

Vue.js 2.0: Apply vue component rendered using v-html, compile the markup

I'm using VueJS 2.0
Is there any way to make the below render as a link?
Here is my vue component:
<template>
<div v-html="markup"></div>
</template>
<script>
new Vue({
data() {
return {
markup: '<router-link :to="{path: 'https://www.google.com'}"></router-link>',
});
},
});
</script>
In the above example, I want to dynamically export a piece of markup, it contains some dynamic contents, such as router-link like above.
But that content did not compile, and exports a <router-link> tag as a final result.
Any way to make it compile programmatically?
What I really want is to find a way to compile a piece of html manually. If v-html doesn`t work, Is there any other way?
v-html works only for pre-compiled html which is basically generated text.
If you want do dynamically change content, simply use if conditions to render your list view based on prop that will tell you the type of the list view.
I don't think it's a good idea to save the markup in your db. It's rather more convenient to save some settings in your db and based on those to render the necessary html. (the prop type in your case). Maybe if you provide a more concrete example, some suggestions will follow. As you can see, the answers were based on your router-link example which I think is not enough to answer your question
I don't think you can instantiate Vue instances via v-html directive. You must override the default to do that, which would take lots of efforts.
If you just want dynamic links, why not try this:
data: {
menu: []
}
and then :
<router-link v-for="item in menu" :to="item.src">{{item.name}}</router-link>
PS: Can you give an example that you must do such things? I am really interesting in what needs it would be.
Given that you want to render a list of links, one way to do this can be like this:
<template>
<router-link v-for="list in lists" :to="{path: list}"></router-link>
</template>
<script>
new Vue({
data() {
return {
lists: ['https://www.google.com', 'https://www.stackoverflow.com']
});
},
});
</script>
Edit:
You can use an approach like following as well using with the help of dynamic components.
Vue.use(VueRouter)
new Vue({
el: "#app",
data: {
dynamicComp: "router-link"
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/2.2.0/vue-router.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.js"></script>
<div id="app">
somethind
<component :is="dynamicComp" :to="{path: 'https://www.google.com'}"></component>
</div>

Categories

Resources