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>
Related
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.
I got a parent component which sends a data object to the children component like this:
<child object-data=" url: 'url here', title: 'Title'"></child>
Then on my children component, I get this object data by doing:
<script>
export default {
props: [
'objectData'
]
}
</script>
Now, for some reason, I can use title without any problem like this {{ objectData.title }} and shows up.
But when it comes to the URL inside an img tag it's not rendering the image.
I attempted doing the following:
<img :src="objectData.url"/> <--- not rendering
<img v-bind:src="objectData.url"/> <--- not rendering
<img v-bind:src="require(objectData.url)"/> <-- throws a warning error because it's not a path but an object I guess.
<img v-bind:src="{objectData.url}"/> <--- throws an error
<img v-bind:src="{{objectData.url}}"/> <--- throws an error
When I check on the dom element, it doesn't even contain a src attribute afterwards.
If I write down the URL without an object, it works.
<img v-bind:src="src/assets/images/icon.png"/>
But I want my URL to come from a parent component.
Any ideas on how to solve this? I added the url-loader on my webpack file:
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader'
}
I also attempted returning objectData.url from a computed/methods fuction:
computed: {
getImageUrl: function() {
return objectData.url;
}
}
And then use it like :src=“getImageUrl” or :src=“{{getImageUrl}}” and I wasn’t lucky either.
I faced the same issue and i fixed it by using require function :
in the parent component App.vue :
<carousel-posts :posts="posts" />
export default {
name: "app",
data() {
return {
posts: [
{
img: require("./assets/logo.png"),
title: "Title 1",
subTitle: "Sub Title 1",
body:"lorem ipsum ..."
}
...
]
};
}
...
}
in the child component i loop through the posts and bind the image src as follows :
<div class="card-body" v-for="(post,idx) in posts" >
<img class="card-img" :src="post.img" />
...
</div>
<script>
export default {
props: ["posts"],
...
So in your case you should have something like :
<child :object-data="objectData"></child>
...
data(){
return{
dataObject:{
url: require('./assets/someimg.png'),
title: 'Title'
}
}
}
Update :
my project tree :
src
|_assets
| |_logo.png
|_components
| |_CarouselPosts.vue
|_App.vue
Two alternatives:
Static links that vue resolves. But they're src-based and don't work with webpack / file-loader:
<img :src="'../assets/filename.png'"/>
Works with webpack. Note the ".default" at the end:
<img :src="require('../assets/filename.png').default"/>
Documentation relevant effective Nov 2019:
https://github.com/vuejs/vue-loader/issues/1612
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>
Hello I am having an issue with Nuxts ssr. I a trying to add 'vue-slick' to my web app and no matter what I do it continues to show "window is not defined".
As you can see I have tried multiple ways to allow vue-slick to be loaded on client side. Using plugins didn't help, using process.client in my component did not work as well.
Components/Carousel/Carousel.vue
<template>
<div class="carousel">
<Slick ref="slick" :options="slickOptions">
<a href="http://placehold.it/320x120">
<img src="http://placehold.it/320x120" alt="">
</a>
...
<a href="http://placehold.it/420x220">
<img src="http://placehold.it/420x220" alt="">
</a>
</Slick>
</div>
</template>
<script>
if (process.client) {
require('vue-slick')
}
import Slick from 'vue-slick'
export default {
components: {
Slick
},
data() {
return {
slickOptions: {
slidesToShow: 4
},
isMounted: false
}
},
methods: {
}
}
</script>
nuxt.config.js
plugins: [
{ src: '~/plugins/vue-slick', ssr: false }
],
plugins/vue-slick
import Vue from 'vue'
import VueSlick from 'vue-slick'
Vue.use(VueSlick);
Thanks for any help you can give!
So this is due to nuxt trying to render the slick component on the server side, even though you have set ssr: false in nuxt.config.
I have had this issue in other nuxt plugins and these steps should fix it.
in nuxt.config.js add this to your build object:
build: {
extend(config, ctx) {
if (ctx.isServer) {
config.externals = [
nodeExternals({
whitelist: [/^vue-slick/]
})
]
}
}
}
and in the page where you are trying to serve it you have to not mount the component until the page is fully mounted. So in your Components/Carousel/Carousel.vue set it up like this:
<template>
<div class="carousel">
<component
:is="slickComp"
ref="slick"
:options="slickOptions"
>
<a href="http://placehold.it/320x120">
<img src="http://placehold.it/320x120" alt="">
</a>
...
<a href="http://placehold.it/420x220">
<img src="http://placehold.it/420x220" alt="">
</a>
</component>
</div>
</template>
Then in your script section import your component and declare it like this:
<script>
export default {
data: () => ({
'slickComp': '',
}),
components: {
Slick: () => import('vue-slick')
},
mounted: function () {
this.$nextTick(function () {
this.slickComp = 'Slick'
})
},
}
</script>
Bascially this means the component isn't declared until the mounted function is called which is after all the server side rendering. And that should do it. Good luck.
I found another simple solution.
simply install "vue-slick" package to your nuxt project.
$ yarn add vue-slick
then component markup like below.
<template>
<component :is="slick" :options="slickOptions">
<div>Test1</div>
<div>Test2</div>
<div>Test3</div>
</component>
</template>
Finally set data and computed properties like this.
data() {
return {
slickOptions: {
slidesToShow: 1,
slidesToScroll: 1,
infinite: true,
centerMode: true,
},
};
},
computed: {
slick() {
return () => {
if (process.client) {
return import("vue-slick");
}
};
},
},
This solution prevents slick component importing globally as a plugin in nuxt config.
I'm building a TabbedDetailView reusable component in vue. The idea is that the tab-detail component receives a list of objects which have a title and a component. It then does the logic so that when you click on a tab, then the component is displayed. The problem is that this components have a prop that is a user_id. How do I insert this prop into the components from outside of the template (directly in the script)?
For example (using single file vue components with webpack):
TabDetail.vue
<template>
<div>
<nav class="tabs-nav">
<ul class="tabs-list">
<li class="tabs-item" v-for='tab in tabs'>
<a v-bind:class="{active: tab.isActive, disabled: !tab.enabled}" #click="switchTab(tab)">{{tab.title}}</a>
</li>
</ul>
</nav>
<div v-for='tab in tabs'>
<component :is="tab.detail" v-if='tab.isActive'></component>
</div>
</div>
</template>
<script>
export default {
name: 'NavigationTabs',
props: ['tabs'],
created: function() {
this.clearActive();
this.$set(this.tabs[0], 'isActive', true);
},
methods: {
clearActive: function() {
for (let tab of this.tabs) {
this.$set(tab, 'isActive', false);
}
}, switchTab: function(tab) {
if (tab.enabled) {
this.clearActive();
tab.isActive = true;
}
},
},
};
</script>
The idea is that this can be reused by only passing a props object with titles and components. eg. tabs = [{title: 'Example1', component: Component1}{title: 'Example2', component: Component2}] I want to be able to instantiate this components with props before passing them. eg. tabs = [{title: 'Example1', component: Component1({user_id: 5})}{title: 'Example2({user_id: 10})', component: Component2}]).
SomeComponent.vue
import Vue from 'vue';
import TabDetail from '#/components/TabDetail'
import Component1 from '#/components/Component1';
const Componenet1Constructor = Vue.extend(Component1);
export default {
data() {
return {
tabs: [
{title: 'Componenent 1', detail: new Component1Constructor({propsData: {user_id: this.user_id}})}
{title: 'Component 2', detail: Component2},
{title: 'Component 3', detail: Component3},
],
};
}, props: ['user_id'],
components: {'tab-detail': TabbedDetail},
}
<template>
<div>
<tab-detail :tabs='tabs'></tab-detail>
</div>
</template>
Component1.vue
export default {
props: ['user_id'],
};
<template>
<div>
{{ user_id }}
</div>
</template>
The approach above raises de error:
[Vue warn]: Failed to mount component: template or render function not defined.
I think this is a good idea because I'm trying to follow the dependency injection design pattern with components. Is there a better approach to this problem without using global state?
This is could be done via Inject Loader when using vue loader with single file vue components but it adds a lot of unnecessary complexity and it's mostly meant for testing. It seems like the preferred way of managing state is by using a global state management store like Vuex.