I am trying to use vue-masonry-wall in my NuxtJS (v2.15.7) app to give it a masonry layout. According to the docs, the vue-masonry-wall package is "SSR friendly". It states to simply add :ssr="{columns: 2}" to masonry so that during SSR, it will be loaded in 2 columns.
I tried this in my code (codesandbox here). But, during SSR, nothing is loaded.
Anyone got any idea on what is happening and why I can't see any of the items? It works fine in client-mode.
Code example:
<vue-masonry-wall :items="items" :options="{width: 300, padding: 12}" :ssr="{columns: 2}" #append="append">
<template v-slot:default="{item}">
<div class="item">
<h5>{{item.title}}</h5>
<p>{{item.content}}</p>
</div>
</template>
</vue-masonry-wall>
One option is to run it just on the client side, so:
1- if loaded as plugin, globally: name the plugin file ending with ".client", for instance: 'vue-masonry-wall.client.js'
2- if used as module, you can wrap it with the tag.
Related
Currently just a BootstrapVue VueJS frontend project I have, I have 4 playing cards that I would ideally like to keep on one line/row (as it does locally), but stacks when I view it in production (using Heroku, if it makes a difference)
Currently the code for this is like:
<div
flex-wrap="nowrap"
class="row d-flex nowrap mt-3"
justify-content="space-between"
width="100vw"
>
<b-container>
<b-row>
<b-col>
<PlayingCard/>
</b-col>
....etc for the other cards....
</b-row>
</b-container>
</div>
I've played around a lot with different classes and justify-contents and all that stuff, but continually get different local vs prod results. And I can confirm the code on Heroku is up to date because it redeploys with each new commit and I've added some new features since attempting to fix this styling issue and those appear properly.
Styling issues like that are most commonly due to scoping issues of your CSS. If you inspect the element locally you will likely see that only the local styling has been applied, while if you inspect the element in production, you will see that either the selector contains more CSS (due to identical selectors in two different components), or another selector is applied altogether.
You are getting this problem, because in dev-mode it only loads the CSS of the components you are viewing. In production mode, all the CSS of all components is combined.
To solve the problem, you have several options:
You can use the scoped attribute on your style tag. Vue will automatically add a data-attribute on your component, and scope the styling using that data attribute. This does commonly not work nicely with things like popups that are moved out of their previous location.
<template>
<div>
<!-- something here -->
</div>
</template>
<script>
export default {
//...
}
</script>
<style scoped>
button {
// This only styles the buttons in this component, rather than every button in the application
}
</style>
If you need to style sub-components as well, you can just use a class on your root element and style everything relative to that. You would need to make that class a unique name in your application, but if you just use the name of your component that shouldn't be a problem.
<template>
<div class="comp-card">
<!-- something here -->
</div>
</template>
<script>
export default {
name: 'Card'
}
</script>
<style lang="scss">
.comp-card {
button {
// All buttons that are descendants of our component are now styled.
}
}
</style>
I see two issues with the given code, addressing these may not resolve your issue, but may remove some interference.
Bootstrap provides the class .flex-nowrap for applying flex: nowrap – it should be noted that nowrap is the default behaviour of flexbox, and this may not be needed anyway.
Some attributes in your div appear to be style declarations, and ought to be in the [style] attribute as shown below, or a <style> tag if using SFCs
<div
class="row d-flex mt-3"
style="justify-content: space-between; width: 100vw"
>
Caching of applicable files can vary between local and production environments.
Browsers accessing production servers may cache older versions of files and make it appear that the change was not made. The server may have newer files than what the browser is using.
UI changes in production might be issue of CSS Optimization in build process of vue cli.
Did you try to serve/run build locally if it works then it might issue on production site/cache/browser etc. Try local build with serve or any other tool to verify.
If css optimization issue in build then re-configure build config with underlying tool like webpack or vite etc whichever used.
If I use fetched data (fetchPolicy: 'cache-and-network') from apollo in v-if, it will throw
vue.runtime.esm.js:619 [Vue warn]: 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 <p>, or missing <tbody>. Bailing hydration and performing full client-side render.
<template>
<div
<div v-if="test">
{{ test }}
</div>
</div>
</template>
but if I use it just as variable to render it works fine.
<template>
<div>
{{ test }}
</div>
</template>
The data in real usage is object, that I need to conditionaly render and pass to another components with v-if.
I have tried geting the data trough get, doing watch over the data and seting them manually, but eventually everything broke.
regarding comment:
if I console the test data it will go -> true on server -> false on client and then true on the client again, if I remove the test from v-if it goes: true on server and true on client
this has nothing to do with structure, in real project it has bunch of components and it works just fine if the data isnt used in condition
For anyone facing the same problem, I have fixed it by setting cache-and-network after the mount and all works just fine.
mounted() {
this.$apollo.queries.getCampaign.setOptions({
fetchPolicy: 'cache-and-network',
})
}
You are trying to make the root element of a component conditionnally disappear, which creates an inconsistency in the virtual DOM.
Can you try:
<template>
<div>
<template v-if="test">
{{ test }}
</template>
</div>
</template>
from my experience, nuxt will failed in SSR if you are using v-if unless you wrap in <no-ssr> or <client-only>
SSR is sometimes difficult to debug and such problems occure quite often.
https://ssr.vuejs.org/#why-ssr
some external libraries may need special treatment to be able to run
in a server-rendered app.
This may be the point. Some vue-components cannot be run via SSR out of the box. This is why I asked about the content from the server.
So the easiest fix could be to wrap your component with the <no-ssr> tag. You can do this for each component separatly to find out which one causes the problem.
Once you have isolated the component you can keep the <no-ssr> tag on it and migrate it's functions to the mounted area of your client component.
If it is a small component you can also use v-show instead.
Before I was working with Vue2JS and I used to creating modal as just component within App.vue root component for example:
<template>
<div>
<app-navbar></app-navbar>
<router-view></router-view>
<app-footer></app-footer>
<my-modal v-if="someBoolean"></my-modal>
</div>
</template>
Now basing on some custom events or Vuex storage I was able to change someBoolean and trigger when I want modal to be visible.
Since in Nuxt we don't have such thing as root App.vue component I'm wondering how to achieve same as above but with Nuxt.
Of course I could use some package as bootstrap-vue but I don't really want to inject this big package just for that one purpose.
You can write code in layouts/default.vue file and this file works on your defaults, work the code at where you used as a layout of your pages(generally almost everywhere.)
Different approach is use portalvue to render components whereever you want. Nice article here but in Turkish.
I have a Vue.js project (Nuxt.js) and as UI I use the Vuetify.
For e2e testing I use the Cypress.
Below is my scenarios of test in Cypress:
I have a problem while creating test for page where I use v-autocomplete component.
The problem is that I can't use Vuetify native classes to get the element I want to test.
below is an example with data-cy selector
<v-autocomplete
v-model="model"
:items="items"
item-text="Description"
item-value="API"
label="Public APIs"
placeholder="Start typing to Search"
data-cy="autocomplete"
></v-autocomplete>
I type some text into search input.
Then in v-autocomplete have been found search results.
And example of one of there is below:
...
<div>
<a class="v-list__tile v-list__tile--link theme--light">
<div class="v-list__tile__content">
<div class="v-list__tile__title">Result item
<span class="v-list__tile__mask">result item</span>
</div>
</div>
</a>
</div>
...
Then I want select one of search items by clicking to one of found results.
And for that I should to use native classes of Vuetify, but it is not have stability (.v-list__tile--link class сan be renamed by developers).
How I can add data-cy selector into result search html item?
Maybe who know any another way to resolve this problem?
I don't think v-list__tile--link can be changed by developers, it seems to be added at runtime DOM by the Vuetify library (unless you mean Vuetify developers, then sure it can change between versions).
In any case, if you want to be more content-oriented in your selectors, use Cypress .parent() selector
For example,
cy.contains('div', 'itemTextToSelect').parent('a').click()
If posssible make sure 'itemTextToSelect' is really distinctive (if you can set it in the test fixture).
BTW, before the user starts typing the autocomplete list is display: none, so you will need to either .type('something') into the input, or .click({force: true}) the item.
Server side rendering page for reference: ssr.html
Now the problem, what if we want to define template inside the <div id="app"></div> in html file itself, not in Vue instance template property? Like this:
<div id="app">You have been here for {{ counter }} seconds.</div>
In this case if we want to pre-render it, we will get next pre-rendered html:
<div id="app" server-rendered="true">You have been here for 0 seconds.</div>
And here is the conflict problem. If we will output pre-rendered html, we lose our template and Vue doesn't know where to output counter inside our <div id="app">.
Is it possible somehow to provide template inside <div id="app"></div> container and in the same time pre-render it? Or provide template near the pre-rendered in html(so Vue will know that here is pre-rendered and here is template and i will use it if any changes happens in the model)?
Is it possible somehow to provide template inside container and in the same time pre-render it? Or
Short but complete answer: No. For Vue SSR, you cannot use in-DOM templates. You have to use string-based templates (including Single File Components).