[VuePress]: Extract links from data - javascript

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.

Related

Vue slot not working - child elements show as empty array

I have the following code where I am trying to pass individual tab components into a tabs component via slots. However, the tabs do not seem to be getting passed. Putting {{ tabs }} on the template only shows an empty array.
<template>
<article>
{{ tabs }} // empty array
<header class="tabs">
<ul>
<li v-for="(tab, index) in tabs" :key="index">
<div class="nav-item"
:class="{ 'is-active': tab.isActive }"
#click="selectTab(tab)">
{{ tab.name }}
</div>
</li>
</ul>
</header>
<section class="tabs-details">
<slot></slot>
</section>
</article>
</template>
<script>
export default {
data: () => {
return {
tabs: []
}
},
methods: {
selectTab(selectedTab) {
this.tabs.forEach(tab => {
tab.isActive = tab.name === selectedTab.name;
});
}
},
created() {
console.log('created[Tabs.vue]-> ', this.$children) // nothing here
this.tabs = this.$children; // not working
}
}
</script>
I have my components registered in the parent like so:
import Tab from '#/components/Tab'
import Tabs from '#/components/Tabs'
export default {
components: {
Datepicker,
Tab,
Tabs
},
...
The template is pretty straight forward. Note, the contents of the individual tabs display fine when selected="true"
<Tabs>
<Tab name="hours" selected="true">Contents here...</Tab>
<Tab name="pay"></Tab>
</Tabs>
I have checked in the browser console and, although the nav-item <li> elements are being created, they have no content.
I'm new to slots so maybe I am missing something obvious or the code syntax I am using is outdated. Any help is much appreciated.
Try to use this.$slots :
this.tabs = this.$slots.default();//returns vnodes, that can be rendered via render function
$children has been deprecated in Vue 3:
In 3.x, the $children property is removed and no longer supported. Instead, if you need to access a child component instance, we recommend using template refs.
If you're trying to get children of <slot></slot>, use this.$slots.default(), as suggested in Boussadjra's answer.
In Vue2, the syntax for getting the default slot contents is this.$slots.default (not a function).
Notes:
you'll get back an array of VNodes, not actual DOM,
you can only access $slots after component has been mounted (e.g: in mounted() hook)
If you want specific data from the vNodes, you'll have to log them and see where exactly it is in your version of Vue (vNodes have had minor changes over time - they're considered internal API).
My advice would be to upgrade to a newer version of 2. If not 2.7.10, at least 2.6.14. Some of the available documentation on Vue 2 describes features which were not yet added in 2.2.3. $slots had a major revamp in 2.6.
For example, I was using slots at the time, but if you ask me now how they looked like, I couldn't tell you with certainty.

Vue3 Manually Render Slot Content

I'm trying to parse out the contents of a slot and render the contents in multiple places. The idea is to create a wizard that allows each step to be contained in a single component. Something like this:
Wizard Component Definition:
<ul>
<li v-for="icon in icons">{{icon}}<li>
</ul>
<section>
<ul>
<li v-for="body in bodies">{{body}}</li>
</ul>
</section>
Wizard Component Script
import {ref} from "vue";
export default {
setup(props, {slots}) {
const icons = ref([]);
const bodies = ref([]);
for (let item of slots.default()) {
// not sure if I need to call these, ex: item.children.icon()
icons.value.push(item.children.icon);
bodies.value.push(item.children.body);
}
return {icons, bodies};
}
}
Wizard Component Usage:
<wizard>
<wizard-page>
<template #icon>someIcon</template>
<template #body>someBody</template>
</wizard-page>
<wizard-page>
<template #icon>someIcon2</template>
<template #body>someBody2</template>
</wizard-page>
</wizard>
The obvious problem here is that everything is VNodes and doesn't just render nicely to the DOM. I've tried using render() and h() but those don't seem to be what I'm looking for. Also tried the v-html binding, but again, that isn't expecting a VNode.
I'm not sure if there's a better way to do this, or if I'm missing something simple here, but I'm not seeing an easy way to split apart a slot and render the contents in different places.
VUE 3
<component :is="body">
You can implement it in the following way
<ul>
<li v-for="body in bodies" :key="uniquekey">
<component :is="body" />
</li>
</ul>
link to docs:
https://vuejs.org/guide/essentials/component-basics.html#dynamic-components

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.

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

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

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