How to bind CSS class into an HTML code tag? - vue.js

I have data like so:
new Vue({
el: '#app',
data: {
icons: [
{ "name": "Car", "css": "car" }, { "name": "Airplane", "css": "airplane" }
]
}
});
And I want to bind the css value to my view inside an HTML <code> element and formatted and rendered as so:
<i class="fa fa-car></i>
Here's my attempt using Vuejs2.0:
<code>%lt;i class="fa" :class="'fa-' + icon.css"></i></code>
Unfortunately, the DOM is rendered like so:
<i class="fa" :class="'fa-' + icon.css"></i>
How do I make this work? Thanks!

If you want to iterate inside your icons array, you can use a template with the v-for directive :
<template v-for="icon in icons">
<i class="fa fa-{{ icon.css }}></i>
</template>

Related

How to render HTML from a property of items being displayed with v-for loop?

I send an array of objects which each have a .html property that has HTML text in it, e.g. <h1>...</h1> or <h2>...</h2>
I want to have the HTML from each item display one after another in the DOM, like this:
<h1>...</h1>
<h2>...</h2>
<h2>...</h2>
<h1>...</h1>
<h2>...</h2>
However, all of these attempts do not work:
<div v-for="item in outlineItems" v-html="item.html"></div>
displays HTML wrapped in divs: <div><h1>...</h1></div> and <div><h2>...</h2></div>
<template v-for="item in outlineItems" v-html="item.html"></template>
displays nothing
<template v-for="item in outlineItems">{{item.html}}</template>
displays the literal HTML instead of rendering it
<template v-for="item in items"><template v-html="item.html"></template></template>
displays nothing
How can I simply display the contents of the .html property of each item so that the HTML in it renders, without any wrapping elements on it?
You could do it using a single wrapper element for the whole lot by concatenating all the HTML in a computed property:
new Vue({
el: '#app',
data () {
return {
outlineItems: [
{ html: '<h1>Heading 1</h1>' },
{ html: '<h2>Heading 2</h2>' },
{ html: '<h3>Heading 3</h3>' }
]
}
},
computed: {
outlineHtml () {
return this.outlineItems.map(item => item.html).join('')
}
}
})
<script src="https://unpkg.com/vue#2.6.11/dist/vue.js"></script>
<div id="app">
<div v-html="outlineHtml"></div>
</div>
Behind the scenes v-html sets the innerHTML of its corresponding DOM node. A <template> tag doesn't create a DOM node so the innerHTML can't be set anywhere.
I would add that v-html is considered an 'escape hatch'. Where possible you should avoid using it and let Vue create the HTML itself. Generally the approach would be to use a suitable data structure to hold the data (rather than a blob of markup) and then render that data structure within the template.
One possible solution is to create multiple unique components. You can even pass in props, and there are no wrappers
Vue.component('greeting', {
template: '<h1>Welcome to coligo!</h1>'
});
Vue.component('titles', {
template: '<h1>title 1</h1>'
});
Vue.component('title2', {
template: '<h2>Welcome to coligo!</h2>'
});
Vue.component('title3', {
template: '<h3>{{text}}</h3>',
props: ['text']
});
var vm = new Vue({
el: '#app',
data: {
items: [
{ type: 'greeting' },
{ type: 'titles' },
{ type: 'title2' },
{ type: 'title3', text: 'test' }
]
}
});
<script src="https://unpkg.com/vue#2.6.11/dist/vue.js"></script>
<div id="app">
<component v-for="(item,i) in items" :is="item.type" :text="item.text" :key="i"></component>
</div>

Removing last item when rendering in vue.js

I am facing an issue where a item is getting rendered even though there is no associated Title with it as shown in my JSON. Please see the attached screenshot which will make you understand my problem (marked in red). I know this is happening due to lid in my JSON for which vue is rendering that without any associated (i.e Title) values. How do I solve this issue. Is there a way to remove the last item when rendering or is there any other way ?. I need the lid in this.dino but do not need it when rendering in my vue-app. Is there a way to pop out the last item from the JSON when rendering.
<div id="vue-app">
<div id="myList" v-for="item in items">
<p>{{item.Title}}</p>
<button v-on:click="loadmore()" class="fluid ui button">Load More</button>
Below is my vue function
new Vue({
el: '#vue-app',
delimiters: ['[[', ']]'],
data: {
dino: d_var,
cati: d_catox,
items: []
},
methods: {
loadmore: function () {
axios.get(this.dino)
.then(response => {
this.items.push(...response.data);
this.dino = "/api/search/" + this.items[this.items.length - 1].lid + "/" + this.cati;
})
}
}
})
Below is my JSON
[
{
"Title": "HealthXP1"
},
{
"Title": "HealthXP2"
},
{
"Title": "HealthXP3"
},
{
"lid": "A1234567890"
}
]
you can use 'v-if' or 'v-show' directive as:
<div id="vue-app">
<div id="myList" v-for="item in items" v-show="item.Title">
<p>{{item.Title}}</p>
<button v-on:click="loadmore()" class="fluid ui button">Load More</button>
that will show that item on the list if the item.Title is defined, otherwise it will not be rendered in DOM

can't display array inside of html using vuejs v-for

I am returning back an array of object to render all on the screen. I am making a Ajax request to get a bunch of titles. Right now I can only manage to make it bring back one by writing vm.CourseTitle=data.d.results[0].Title; Instead, I want it to return all, I have tried vm.CourseTitle=data.d.results.Title; but that breaks it. How do I need to format the vuecode to make this display all the titles? In theory, this would be enclosed within a return{}. No idea why I can't get this to work!
new Vue({
el: "#app",
data: {
CourseTitle: [{ "Title":"Ford", "models":[ "Fiesta", "Focus", "Mustang" ] },
{ "Title":"BMW", "models":[ "320", "X3", "X5" ] },
{ "Title":"Fiat", "models":[ "500", "Panda" ] }
],
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<h2>Todos:</h2>
<li v-for="course in CourseTitle"></li>
{{CourseTitle}}
</div>
You're not correctly manipulating html. It should be:
<li v-for="course in CourseTitle">{{course.Title}}</li>
Further, li tag should be wrapped with ul tag.

VueJs: Pass Dynamic Components as props to another Component and render them

I am trying to build a table component.
I want to define and pass the column metadata for the grid as an array prop and also pass the actual data as another prop to the grid.
I was able to achieve that without many issues.
But, now I would like to pass a dynamic component as a part of each column definition so that the user can define/control the way the cell gets rendered (content with edit delete buttons in same cell etc.)
Is there a way to pass a dynamic component as a prop and then have this component rendered?
<parent-comp>
<tr class="" v-for="result in dataSource">
<template v-for="column in columns">
<td>
<template v-if="column.customComponent">
######## How do I render this customComponent ########
</template>
</td>
</template>
</tr>
</parent-comp>
where the dataSource data can be something like
[
columns: [{
name: "something",
customComponent: SomeCustomComponent
}, {
name: "another thing",
customComponent: AnotherOtherCustomComponent
}]
]
Will be happy to elaborate/clarify on this if the ask above is not clear.
As suggested in the comments above, you can use a dynamic component in your template and pass the definition of the component in your property.
console.clear()
const ColumnOne = {
template: `<h1>I am ColumnOne</h1>`
}
const ColumnTwo = {
template: `<h1>I am ColumnTwo</h1>`
}
Vue.component("parent-comp",{
props:["columns"],
template:`
<div>
<component v-for="column in columns"
:is="column.customComponent"
:key="column">
</component>
</div>
`
})
new Vue({
el:"#app",
data:{
columns:[{
name: "something",
customComponent: ColumnOne
}, {
name: "another thing",
customComponent: ColumnTwo
}]
}
})
<script src="https://unpkg.com/vue#2.2.6/dist/vue.js"></script>
<div id="app">
<parent-comp :columns="columns"></parent-comp>
</div>

Wrap element using v-if, otherwise just have content itself

I have a set of elements being generated in a v-for directive that, if the object has a url property, get wrapped in an <a> tag - otherwise, I need it to just emit the element itself.
For example,
var data = {
events: [
{name: "Foo"},
{name: "Bar", url: "google.com"}
]
};
and the corresponding HTML:
<div v-for="event in events">
<span>{{event.name}}</span>
</div>
What I need is to wrap the <span> in an <a v-bind:href="url"> only if there is a url property present.
I understand I could use a v-if and use two spans, such as:
<span v-if="!event.url">{{event.name}}</span>
<a v-bind:href="event.url" v-if="event.url">
<span>{{event.name}}</span>
</a>
However, in my use case the <span> element here could be massive and I don't want to repeat myself just to wrap the element.
Is there a way to achieve a conditional wrap such as the above?
You can use v-html for example and render your logic inside a function:
function wrapSpan(el, link) { // link wrapper function
return `${el}`;
}
new Vue({
el: '#app',
data: {
events: [
{name: "Foo"},
{name: "Bar", url: "google.com"}
]
},
methods: {
element: function(i) {
const name = this.events[i].name;
const url = this.events[i].url || null;
const span = `<span style="color: green">${name}</span>`; // long span
return (url) ? wrapSpan(span, url) : span;
}
}
});
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<div v-for="(event, index) in events">
<span v-html="element(index)"></span>
</div>
</div>