I have the following Vue component called TeamCard:
<template>
<div class="m-8 max-w-sm rounded overflow-hidden shadow-lg">
<!-- removed fro brevety -->
div class="text-gray-900 font-bold text-xl mb-2">{{ name }}</div>
<p class="text-gray-700 text-base"> {{ description }} </p>
<!-- removed fro brevety -->
</div>
</div>
</template>
<script>
export default {
name: "TeamCard",
props: {
name: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
}
};
</script>
<style scoped>
#import "https://unpkg.com/tailwindcss#^2/dist/tailwind.min.css";
</style>
I call the component in an HTML page (Spring boot template) the following way:
<teamCard v-bind:name="'Hawks'" v-bind:description="'Best team'" ></teamCard>
<script src="https://unpkg.com/vue"></script>
<script th:src="#{/TeamCard/TeamCard.umd.min.js}"></script>
<script>
(function() {
new Vue({
components: {
teamCard: TeamCard
}
})
})();
</script>
When I go to the page in the browser that has the second snippet, nothing is shown. There are no errors in the console. What am I doing wrong? How can I make the component be shown?
I've never seen Vue used this way but it looks like there is no mounting element for the app. Try this:
<div id="app">
<team-card v-bind:name="'Hawks'" v-bind:description="'Best team'"></team-card>
</div>
and
(function() {
new Vue({
el: '#app',
components: {
teamCard: TeamCard
}
})
})();
Related
I'm using the npm module if-emoji to detect whether a user can view emojis or not. There is a similar tool in Modernizr.
If the user can't view emojis, I'm displaying an image of the emoji instead. So my Vue HTML looks like this:
<h2 v-if="this.emojis">😄</h2>
<h2 v-if="!this.emojis"><img src="https://example.com/emoji.png">/h2>
Does this still download the image for users who can view emojis, therefore using bandwidth unecessarily?
And is there a more efficient way of doing this, so that I don't need to go and add v-if every time I use an emoji? Can there be some sort of lookup table, if there's an emoji and !this.emojis?
You can solve this also by creating your own vue.component
<emojiorimage usesmily="false" smily="😄" imgpath="https://i.stack.imgur.com/QdqVi.png"></emojiorimage>
that capsulates the decision if image or smily inside itself.
var app = new Vue({
el: "#app",
data: function(){
return {
extendedCost: 0,
}
},
components: { "emojiorimage" : {
template : `
<h2>
usesmily is {{usesmily}}<br/>
<div v-if="usesmily === 'true'">{{ smily }}</div>
<div v-else><img :scr="imgpath" width="50" height="50" :alt="imgpath" /></div>
</h2>`,
props: ["usesmily", "smily", "imgpath"],
}}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<h1>Emoji or Image</h1>
<emojiorimage
usesmily="false"
smily="😄"
imgpath="https://i.stack.imgur.com/QdqVi.png"
></emojiorimage>
<emojiorimage
usesmily="true"
smily="😎"
imgpath="https://i.stack.imgur.com/QdqVi.png"
></emojiorimage>
</div>
You can then feed it the isemoji from your npm package that you query once and store somewhere.
For seperate vue files it would look kind of like this:
emojiorimage.vue
<template>
<h2>
<div v-if="usesmily === 'true'">{{ smily }}</div>
<div v-else><img :scr="imgpath" width="50" height="50"
:alt="imgpath" /></div>
</h2>
</template>
<script>
export default {
props: ["usesmily", "smily", "imgpath"],
};
</script>
App.vue
<template>
<div id="app">
<h1>Emoji or Image</h1>
<emojiorimage
usesmily="false"
smily="😄"
imgpath="https://i.stack.imgur.com/QdqVi.png"
/>
<emojiorimage
usesmily="true"
smily="😎"
imgpath="https://i.stack.imgur.com/QdqVi.png"
/>
</div>
</template>
<script>
import emojiorimage from "./components/emojiorimage.vue";
export default {
components: {
emojiorimage,
},
};
</script>
index.html
<div id="app"></div>
index.js
import Vue from "vue";
import App from "./App";
Vue.config.productionTip = false;
new Vue({
el: "#app",
template: "<App/>",
components: { App }
});
to get:
Learning resources:
https://v2.vuejs.org/v2/guide/components.html
https://v2.vuejs.org/v2/guide/single-file-components.html
This should work as well:
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<h2 v-if="true">😄</h2>
<h2 v-else><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/e/e0/SNice.svg/1200px-SNice.svg.png" width=15 height=15></h2>
<h2 v-if="false">😄</h2>
<h2 v-else><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/e/e0/SNice.svg/1200px-SNice.svg.png" width=15 height=15></h2>
The v-if part only is part of the sourcecode if the statement is true - else the else statement is in instead. If the img tag is not part of the loaded source the image wont be loaded.
<h2 v-if="emojis">😄</h2>
<h2 v-else><img src="https://example.com/emoji.png"></h2>
Is probably the only way to improve your code as #Patrick Artner pointed out.
You do not need this. in the template
To the question if the image is loaded if it is not shown the simple answer is no. Vue only renders v-ifs when they are needed and does not render it - the image is loaded only if !emojis.
Thanks to #PatrickArtner who posted the idea of using a Vue component to render the emoji / image. Here is my final solution, which resizes the emoji / image to fit the surrounding text — it uses fitty to resize the emojis, and sets the images to height:1em;.
Emoji.vue
<template>
<div class="emojiContainer">
<span id="fittyEmoji" v-if="emojis">{{ insert }}</span>
<img v-if="!emojis" :src="insert" class="emojiImage">
</div>
</template>
<style scoped>
.emojiContainer {
display: inline;
}
.emojiImage {
height:1em;
}
</style>
<script src="fitty.min.js"></script>
<script>
import fitty from 'fitty';
import ifEmoji from 'if-emoji'
export default {
name: 'Emoji',
props: ['name'],
data: function() {
return {
insert: "",
emojis: false,
}
},
methods: {
insertEmoji(){
var names = ['frog', 'fire']
var emojis = ['🐸','🔥']
var urls = ['https://example.com/frog.png',
'https://example.com/fire.png']
if (this.emojis) {
this.insert = emojis[names.indexOf(this.name)];
} else {
this.insert = urls[names.indexOf(this.name)];
}
fitty('#fittyEmoji')
}
},
beforeMount() {
if (ifEmoji('🐸')) {
this.emojis = true;
} else {
this.emojis = false;
}
this.insertEmoji();
}
}
</script>
Then in your parent components you can just insert like this:
<template>
<div id="app">
<h1><Emoji name='frog'/> Frog Facts</h1>
<p>Lorem ipsum <Emoji name='fire'/><p>
</div>
</template>
<script>
import Emoji from '#/components/Emoji.vue'
export default {
components: {
Emoji,
},
};
</script>
The application throws me an error in the console.
The property or method "logo" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option or for class-based components, by initializing the property
This is what HTML and script look like.
<template id="items-template">
<span class="items">
<img v-if="logo.name" src="/images/name.png"/>
</span>
</template>
#section scripts {
<script type="text/javascript">
Vue.component('items', {
props: ['logo'],
template: '#items-template'
});
</script>
}
Here's an example of how it should work:
<div id="app">
<items :logo="logo"></items>
</div>
<script type="text/x-template" id="items-template">
<div>
<span class="items">
<img v-if="logo.name" src="https://thumbs-prod.si-cdn.com/d4e3zqOM5KUq8m0m-AFVxuqa5ZM=/800x600/filters:no_upscale():focal(554x699:555x700)/https://public-media.si-cdn.com/filer/a4/04/a404c799-7118-459a-8de4-89e4a44b124f/img_1317.jpg"/>
</span>
</div>
</script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var itemComponent = {
template: "#items-template",
props: {
logo: Object
}
}
new Vue({
el: "#app",
data: {
logo: {
name: 'someName'
}
},
components: {
'items': itemComponent
}
})
</script>
In this example, div#app is parent, and it passes down logo to items-component logo prop.
Wanted outcome : create a card component that I can use through several different pages. I want to define directly on each page the top image that I want to use within the component.
The Card Component :
<template>
<div>
<b-card img-alt="Card image" img-top>
<b-card-img :src="imageUrl" alt="Image" bottom></b-card-img>
<b-card-text>
Some quick example text to build on the card and make up the bulk of the
card's content.
</b-card-text>
</b-card>
</div>
</template>
<script>
export default {
props: {
imageUrl: String,
required: true
}
};
</script>
The index page :
<template>
<div>
<Card ></Card>
</div>
<
</template>
<script>
import Card from "~/components/Card.vue";
export default {
components: {
Card
},
data: function() {
return {
imageUrl: require("../assets/imgs/logo.png")
};
},
};
</script>
<style scoped>
}
</style>
Can you tell me what's wrong? If I create an about-us page reusing the card component, I would like to change imageUrl prop.
You have the imageUrl prop in your Card component, but aren't using it on your index page. You should be able to do <Card :image-url="imageUrl"> on your index page.
One thing that is wrong however, is how you define your prop in your component.
Change it to this:
props: {
imageUrl: {
type: String,
required: true
}
}
Vue.component('card', {
el: '#card-template',
props: {
imageUrl: {
type: String,
required: true
}
}
})
new Vue({
el: '#app',
data() {
return {
imageUrl: 'https://via.placeholder.com/1920x720'
}
}
})
<link href="https://unpkg.com/bootstrap#4.4.1/dist/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://unpkg.com/bootstrap-vue#2.13.0/dist/bootstrap-vue.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.13.0/dist/bootstrap-vue.js"></script>
<div id="app">
<card :image-url="imageUrl"></card>
</div>
<template id="card-template">
<b-card img-alt="Card image" img-top>
<b-card-img :src="imageUrl" alt="Image" bottom></b-card-img>
<b-card-text>
Some quick example text to build on the card and make up the bulk of the
card's content.
</b-card-text>
</b-card>
</template>
Currently I am using as
<template> ... </template>
<script src="test1.js"> </script>
Where all my business logics are written in test1.js. Now I need to use test2.js with same template. I need to reuse the template with different component.
My current code goes like this...
common.vue
<template>
<div>
{{ page }}
</div>
<template>
<script src="../scripts/test1.js"></script>
Where in test1.js
export default {
name: 'home',
components: {
test: test
},
data() {
return {
page: "test1",
}
}
}
But also i need to use common.vue in my test2.js. Which i am not able to import both the .js seperately.
In angular we can write the fallowing late bindig which binds .html and .js(in ui.router)
.state('home', {
url:'/',
templateUrl: 'templates/home.html',
controller: 'HomeController'
})
Is there something similar to this?
You can share the template through the id attribute.
Vue.component('my-comp1', {
template: '#generic',
data () {
return {
text: 'I am component 1'
}
}
})
Vue.component('my-comp2', {
template: '#generic',
data () {
return {
text: 'I am component 2'
}
}
})
new Vue({
el: '#app'
})
<div id="app">
<my-comp1></my-comp1>
<my-comp2></my-comp2>
</div>
<template id="generic">
<div>
<p>{{ text }}</p>
</div>
</template>
<script src="https://unpkg.com/vue"></script>
Is it possible to declare a component inside another component in Vue.JS?
this is what i'm trying to do:
<!-- this is declared inside some-component.vue file -->
<script>
export default {
components:{
'cmptest' : {
template:'#cmptest',
props:['mprop']
}
},
data : () => ({
val:'world'
})
};
</script>
<template>
<div>
<template id="cmptest">
{{mprop}}
</template>
<cmptest mprop="hello"></cmptest>
<cmptest :mprop="val"></cmptest>
</div>
</template>
I'd like to avoid globally registering the child component if possible (with Vue.component(...))
In other words, I'd like to specify child's <template> inside the parent component file (without doing a huge line template:'entire-html-of-child-component-here')
Sure.
Like this:
https://jsfiddle.net/wostex/63t082p2/7/
<div id="app">
<app-child myprop="You"></app-child>
<app-child myprop="Me"></app-child>
<app-child myprop="World"></app-child>
</div>
<script type="text/x-template" id="app-child2">
<span style="color: red">{{ text }}</span>
</script>
<script type="text/x-template" id="app-child">
<div>{{ childData }} {{ myprop }} <app-child2 text="Again"></app-child2></div>
</script>
new Vue({
el: '#app',
components: {
'app-child': {
template: '#app-child',
props: ['myprop'],
data: function() {
return {
childData: 'Hello'
}
},
components: {
'app-child2': {
template: '#app-child2',
props: ['text']
}
}
}
}
});