conditionnal rendering component vue.js - vuejs2

In my vue.js app I would like to rendering some html element only if I have selected item before.
If selected item then I displayed it again into small mini card version.
This seems to be very easy to do that with this code :
<div v-if="objSelected">
<p>test</p>
<mini-card v-bind:item="objSelected"></mini-card>
</div>
But the v-if mechanism works because it display well the p tag into this. But never display the mini-card component
Also I trying that :
<mini-card v-if="objSelected" v-bind:item="objSelected"></mini-card>
but the result is the same...
My mini-card component works into another component if I render this directly on the mounted component like this :
<mini-card v-bind:item="obj1"></mini-card>
The issue seems to be dealing with dynamic rendering component.
But why this simple dynamic rendering code doesn't work ?
What can I do for displaying component dynamically or after user action ?
EDIT :
declaration of Test component which contain mini-card (and display statically) :
import MiniCard from "./mini-card"
export default
{
name: "Test",
components: {MiniCard},
declaration of Game component which contain mini-card (and don't displayed dynamically) :
import MiniCard from "./mini-card"
export default
{
name: "game",
components: {MiniCard},
My instance vue declaration :
new Vue({
components: { App },
router,
store,
template: '<App/>'
}).$mount('#app')
I use the vue-router which display other component without included those into this declaration.
Finally
My problem become to this line for render image into component (if component call statically, this component with image run but if I call that dynamically it generate error) :
<img :src="require('#/assets/' + this.item.logo)" width="50" height="50" alt="logo"></img>
Issue say :
[Vue warn]: Error in render: "Error: Cannot find module './'"
Impossible to render dynamic image with vuejs ?

<img :src="require('#/assets/' + this.item.logo)" width="50" height="50" alt="logo"></img>
Try something like this:
<img :src="`#/assets/${item.logo}`" width="50" height="50" alt="logo"></img>
To updating your component with dynamic data:
// extend and register in one step
Vue.component('mini-card', {
//Props camelCase in JS
props: {
item: {
type: Object
}
},
// show your data
template: `<strong>{{ item.name }}</strong>`
});
new Vue({
el: '#container',
data: {
selected: null,
items: [{ name: "Water" }, { name: "Fire" }, { name: "Cold" }]
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="container">
<select v-model="selected">
<option
v-for="item in items"
:value="item">
{{ item.name }}
</option>
</select>
<mini-card :item="selected" />
</div>

Related

Vue: unexpected token: ':' in image path

I'm passing an image path to a child Vue component but get:
unexpected token: ':' in https://path.to.image/image.jpg
I've created a Vue sandbox that illustrates the problem.
HTML
<div class="js-product-map">
<product-map
:logo="https://path.to.image/image.jpg"
/>
</div>
Component
<template>
<div>
<img :src="image" />
</div>
</template>
<script>
export default {
props: {
logo: {
type: String,
required: true,
},
},
computed: {
image() {
return this.logo;
},
},
};
</script>
What am I doing wrong?
Can you clarify what it is you're trying to do exactly? Because it's not entirely clear to me.
The error you're getting is coming from this:
<div class="js-product-map">
<product-map
:logo="https://path.to.image/image.jpg"
/>
</div>
It's complaining about the : on the logo attribute. But I question why you're doing it this way to begin with. Typically you only want a single selector in index.html as the root of your app. Unless you're trying to have multiple Vue instances.
The way that you have your ProductMap component set up in your sandbox seems like it would accomplish what you're trying to do in index.html.

passing object to component using v-for

I am trying to send a series of objects that are in an array to a child component using v-for, but when I try to access them from the child component, it tells me that the props are not defined.
Im using Quasar Framework actually
This is how I pass the data:
<div class="row justify-center">
<foo
v-for="brand in brands"
:key="brand.id"
:brand="brand"
></foo>
</div>
<script>
import foo from "src/components/foo.vue";
export default {
components: {
foo
},
data() {
return {
brands: []
};
},
methods: {
async getData() {
let x = await get.getData();
this.brands = x.data;
console.log(this.brands);
}
},
mounted() {
this.getData();
}
};
</script>
brands is an array that obtains two objects from a request made to a local database, which I have already verified that it receives the data correctly
And this is the component file and how I try to get the properties:
<q-card class="my-card" flat bordered>
<q-img
:src="require(`../assets/${brand.img}`)"
:alt="brand.img + ' Logo'"
/>
<div class="text-h5 q-mt-sm q-mb-xs">{{ brand.name }}</div>
<div class="text-caption text-grey">
<p>
{{ brand.price }}
</p>
</div>
<script>
export default {
name: "foo",
props: ["brand"],
data() {
return {
expanded: false
};
},
};
</script>
but when I try to execute the code it gives me the following error:
Error in render: "Error: Cannot find module './undefined'
I know one way to make it work, and it is by creating a property for each of the object's values, for example:
<component
v-for="brand in brands"
:key="brand.id"
:name="brand.name"
:price="brand.price"
></component>
But I dont think thats the correct way to do this....
try to change
import component from "src/components/component.vue";
to
import foo from "src/components/component.vue";
on your components section you just call foo instead of foo:component
I am not sure, but:
Looks like ${brand} is empty. Your function GetData() is async, so the <foo> is created before the GetData() has its data set/returned.
You can change
<foo v-for="brand in brands" :key="brand.id" :brand="brand"></foo>
To
<foo v-if="brands.length> 0" v-for="brand in brands" :key="brand.id" :brand="brand"></foo>
To make sure that the element is renderd after the data if set.
Note: v-if is when the html is rendered, v-show is just a css display hide, but the html is always renderd

Nuxt clientside only component

I have a global component for rendering thumbnail collections. This global component uses Glightbox library to open / view the images. However it does not work serverside because it checks the browser / navigator. But wonder how I can disable / exclude the use of glightbox when rendering serverside ?
Here is my component :
<template>
<div v-if="items.length > 0" class="gallery">
<div v-for="(item, myindex) in items" :key="myindex" class="thumbcontainer">
<a :href="item.source.version_900x900.url" :class="setLightBoxClass()">
<div
v-lazy:background-image="item.source.version_250x250.url"
class="innerthumb"
style="--aspect-ratio:1.33"
/>
</a>
</div>
</div>
</template>
<script>
import GLightbox from 'glightbox'
export default {
name: 'BaseGallery',
inheritAttrs: false,
props: {
items: Array,
galleryId: Number
},
data() {
return {
lightbox: undefined
}
},
mounted() {
this.$nextTick(function() {
this.initLightBoxes()
})
},
methods: {
setLightBoxClass() {
return {
glightbox: true,
['glightbox-' + this.galleryId]: true,
thumblink: true
}
},
initLightBoxes() {
this.lightbox = GLightbox({ selector: 'glightbox-' + this.galleryId })
}
}
}
</script>
The "import" statement triggers the error of "Navigator undefined". Any suggestion how to solve this ? Is there a clean way to to make a serverside and clientside version of the same component ?
I use it in other components like this :
<base-gallery :items="items" :gallery-id="123" />
I tried adding client-only, but that does not fix it.
I've also put the loading of the component in in plugin like this :
Vue.component(componentName, componentConfig.default || componentConfig)
And then in my nuxt config :
plugins: [
{ src: '~/plugins/baseGallery', mode: 'client' }
],
But that doesn't work eighter :
The client-side rendered virtual DOM tree is not matching server-rendered content. This is likely caused by incorrect HTML markup, for example nesting block-level elements inside , or missing . Bailing hydration and performing full client-side render.
Ok, I found out what was wrong.
I was trying to do this :
<base-gallery client-only :items="items" :gallery-id="123" />
But it should be like this :
<client-only>
<base-gallery :items="items" :gallery-id="123" />
</client-only>

Vuejs modal component cannot reference method

I have a modal dialog which is trying to use a method on the Vue app instance but getting the error
app.js:32117 [Vue warn]: Property or method "calcFees" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
The app declaration
Vue.component('sale', require('./components/Sale.vue'));
const app = new Vue({
el: '#app',
data: {
showModal: false
},
methods: {
calcFees: function (event) {
alert('GOOD');
}
}
});
Sale.vue component minimised for now
<template name="sale">
<input type="text" placeholder="Sale Price" class="form-control" #blur="calcFees">
</template>
The sale component is simply included in the main page here
<sale v-if="showModal"></sale>
The modal dialog works fine, displays the above text input however the above error is shown in the console and the blur event doesn't call the method.
It seems it has something to do with the component template, because i tested the blur event successfully by putting a text input in the main blade page directly.
Any ideas why it doesn't work in this way? I saw a comment somewhere about something to do with it being a <template> but it didn't explain how to fix.
Components cannot access methods declared in other components or the root Vue directly.
The problem with this code is that the calcFees method is declared in the root Vue, but you are trying to call it from the Sale.vue component.
There are several ways to make this work. One is you can move calcFees to the component. Another is you can $emit an event to the parent with whatever it needs to use in calcFees.
Sale.vue
<template name="sale">
<input type="text" v-model="price" placeholder="Sale Price" class="form-control" #blur="onSale">
</template>
<script>
export default {
data(){
return {
price: null
}
},
methods: {
onSale(){
this.$emit('sale', this.price)
}
}
}
</script>
Vue
<sale v-if="showModal" #sale="calcFees"></sale>
const app = new Vue({
el: '#app',
data: {
showModal: false
},
methods: {
calcFees: function (price) {
alert(price);
}
}
});

Vue the same component tag name will impact by other data source

I design a CMS for content editing, and I have a template_id list for user to select what kind of template he/she wants.
my code like below:
CodePen
https://codepen.io/Aircon/pen/NjeMwG/
HTML
<div id="app">
<section v-for="s, index in sections">
<h3>Section {{ index }} </h3>
<select #change="templateChange(index)" v-model="s.selected_option">
<option value="">-----</option>
<option v-for="t, key in templates" :value="key">{{ key }}</option>
</select>
<my-component></my-component>
</section>
</div>
JAVASCRIPT
var sections = [
{
selected_option: ''
},
{
selected_option: ''
}
];
var templates = {
template01: {
html: "<p>TEMPLATE01</p>"
},
template02: {
html: "<p>TEMPLATE02</p>"
}
};
var vm = new Vue({
el: "#app",
data: {
sections: sections,
templates: templates
},
methods: {
templateChange(index) {
console.log("option:" + sections[index].selected_option);
Vue.component("my-component", {
template: templates[sections[index].selected_option].html
});
}
}
});
The problem is
if i have more then one section, component will be impact by others selection, how can i fix it ?
Changing a component's template within another component is going to change the definition globally and is bad practice.
You should initially register two components, one for each template:
Vue.component("my-component-1", { template: "<p>TEMPLATE01</p>" });
Vue.component("my-component-2", { template: "<p>TEMPLATE02</p>" });
Change your Vue instance's data properties to include the two component names instead of the templates:
data: {
sections: sections,
components: ['my-component-1', 'my-component-2']
},
Change your v-for to loop through the new components array instead:
<option v-for="component in components" :value="component">{{ component }}</option>
And, finally, change the displayed component to be a dynamic component using the :is directive to bind the type of component to the selected option:
<component :is="s.selected_option"></component>
Here's a working codepen.