Vue won't render anything if there are *any* errors - vue.js

I'm learning Vue, and I notice that when other people view their webpages, it mostly displays alright even if they haven't finished the code. This is the behavior I'm used to from vanilla html/css/javascript.
But every time I have any problem with a component, e.g. undefined variable, it breaks the whole component and nothing gets displayed. For example, in a Single File Component if I have
<template>
<form>
<label>Write the title:</label>
<input v-model="title"></input>
/* Bunch of other stuff */
<form>
</template>
<script>
export default {
data() {
return {
/* I forget to write title here */
}
}
}
</script>
<style>
</style>
The whole template won't get displayed. Why is this happening and how can I fix it? It makes development much harder.
Btw I'm using Vue CLI 3 and used Vue UI to create the project, and "npm run serve" to view the site.

Related

How to properly load Bootstrap5's Masonry into Nuxt?

I am trying to use the Masonry plugin with Bootstrap5 and NuxtJS. When I follow the example here
https://getbootstrap.com/docs/5.0/examples/masonry/ and incorporate it into my own codesandbox, I notice that my demo is not in the correct masonry format. See the gaps? My sandbox
My example:
Bootstrap's example:
What do I need to do to get my demo into format shown on the Bootstrap Masonry example page?
I checked how to load the script from a CDN either globally or locally. It was working but at one condition: you needed to NOT start on the masonry page.
Meaning that if you loaded the app on a specific page, then moved to the one with the masonry it was working. But not if you started on this specific page. So, a pretty subpar solution.
This article was really helpful to understand how to wait until the CDN script is fully loaded: https://vueschool.io/articles/vuejs-tutorials/how-to-load-third-party-scripts-in-nuxt-js/
Then I realized that we are far better installing it directly as an NPM dependency. Therefore, I proceeded to the masonry repo. Found a great message on how to setup the whole thing in Nuxt.
And after a removal of some useless stuff and some modern dynamic import, here we are
<template>
<main>
<h1>Bootstrap and Masonry</h1>
<div class="row" id="masonry">
<!-- ... -->
</main>
</template>
<script>
export default {
async mounted() {
if (process.browser) {
let { default: Masonry } = await import('masonry-layout')
new Masonry('#masonry', { percentPosition: true })
}
},
}
</script>
The final solution is looking pretty well and there is not a lot of code. On top of that, the code is properly loaded. And you can load it on a click or any other event.

attribute binding in vue 3

I'm new to Vue and currently trying to dynamically change the video or image source link by passing the data in through a prop. I created a component with specific template structure that I would like to pass in the source from the main app.js page. I've tried binding it in both areas but unsure if I'm doing it correctly. I tried using regular divs and stuff to embed the video in app.js and it shows the content perfectly.
parent element contains 'Video' component-
<Video theme="IL" :vidSrc="srcIL.vid"></Video>
import Video from "./components/Video.vue";
export default {
name: "App",
components: {
Video
},
data() {
return {
srcIL: {
vid: "./assets/invi-lines/invisible-lines-film.mp4"
}
};
}
child 'Video component'
<template>
<div class="introVid top">
<video controls :src="vidSrc"></video>
</div>
</template>
<script>
export default {
props: ["theme", "vidSrc"]
};
</script>
This seems like you have it set up properly, and it is hard to know exactly what is causing the issues from the info provided, but I'm going to make a guess that it might be that the asset is not getting bundled.
I tried using regular divs and stuff to embed the video in app.js and it shows the content perfectly
I suspect you had something like:
<video controls src="./assets/invi-lines/invisible-lines-film.mp4"></video>
which would have taken the resource from the assets and packaged it for use.
see relative-path-imports for details.
You can try forcing these to load using require somewhere in the project, which will force the compiler to copy the asset, but really, if you have dynamic assets (assuming there's more than a handful and they can change) you should have them in the public folder already, not in the source folder. So my recommendation is that you move the dynamic assets to the public folder (assuming that was your issue to begin with)

createApp({}).mount('#app') clears #app's child elements in vue3

So I'm trying to add Vue3 to an existing asp.net core project. What I'd like to happen is for my razor app to render as normal, then use custom vue components to give my frontend a more reactive feel. However, when I mount an empty vue app to my wrapper div (parent of all other body content), it seems to be deleting all innerHTML of that wrapper div, completely removing all server rendered body content.
In my _Layout.cshtml file, I'm wrapping all content in a div with id 'app'.
<body>
<div id='app'>
#RenderBody()
</div>
<script src="~/js/vue-app/dist/js/chunk-vendors.76316534.js"></script>
<script src="~/js/vue-app/dist/js/app.bf4c5ba9.js"></script>
</body>
in main.js
import { createApp } from 'vue'
const vueApp = createApp({}).mount('#app');
// component definitions below
With the app set up like this, when I run my .net project I see a blank white browser window instead of the razor compiled html that I expect. In Vue2, it was possible to do this:
const vueApp = new Vue({
el: '#app',
data: {
....
},
methods: {
....
}//, etc
});
Which would result in the app being rendered as normalthe vue app bound to #app, making vue available to the child content (model binding, vue click handling, etc).
I've tried playing around with the isHydrate optional parameter on mount(), but it causes no change in the result.
Am I missing something here? How do you slowly migrate an existing project to use vue3 if you can't mount the app without clearing content? Any guidance is much appreciated.
Thank you
Notes:
vue-next runtime-dom source If this method is the mount method getting called, I'm not sure why container.innerHTML would not be getting set in the component. {} is not a function, and render/template is not defined for it.
vue-next runtime-core apiCreateApp source If this is the method getting called....I have no idea.
Update
Vue 3, without template renderer, will not be able to handle the templates after it has been compiled. To fix that, you can import vue/dist/vue.esm-browser (and vue.runtime.esm-browser.prod for prod), instead of the default vue. This will allow run-time component rendering.

vue-apollo: GraphQL queries only run when using <ApolloQuery> tags. Never works in script code. this.$apollo.queries is empty

I know I'm probably missing something really super basic here, I'm trying to run a graphql query with vue-apollo... if I use tags, it works fine
If I try to run queries from inside my code, like in the basic examples in the docs (links below) then nothing happens. The server never receives any request, and this.$apollo.queries is empty.)
Basic examples from the docs:
https://akryum.github.io/vue-apollo/guide/apollo/#queries
https://vue-apollo.netlify.com/guide/apollo/queries.html#simple-query
I've defined the query in the "apollo" property/object... how do I actually execute it when the page loads? Note that I'm using typescript, which is why it's using a "get apollo()" method.
Here's my code...
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
export default class List extends Vue {
myQueryName='my default value';
get apollo() {
return {
myQueryName: {
query: require('../graphql/listMeta.graphql'),
// prefetch: true <-- tried with and without this
}
}
}
}
</script>
<template>
<div>
<!-- THIS DOESN"T WORK ... -->
queries are: {{this.$apollo.queries}} <!-- THIS JUST SHOWS AN EMPTY OBJECT: {} -->
<hr>
myQueryName value is: {{myQueryName}} <!-- THIS JUST SHOWS "my default value" -->
<hr>
<!--
THIS DOES WORK...
<ApolloQuery :query="this.apollo.myQueryName.query" >
<template slot-scope="{ result: { loading, error, data } }"></template>
</ApolloQuery>
-->
</div>
</template>
Note that this.$apollo.queries is empty in the template, probably a clue... but still no idea why its empty. From the docs and examples I've seen it should be populated from my get apollo data method.
Looks basically the same as https://github.com/OniVe/vue-apollo-typescript-example/blob/master/pages/index.vue as far as I can tell, I don't know what the difference is.
I've tried rebuilding the project from scratch multiple times (with and without nuxt), over teh course of months and many different versions of vue/nuxt/apollo/vue-apollo... the queries never run (from memory) unless I use tags.
What am I missing?
You are missing #Component decorator, it is needed to set initial properties of Vue component, so vue-apollo can detect it when component created and make a smartquery property.
import { Component, Vue } from 'vue-property-decorator'
import listMetaDocument from '../graphql/listMeta.gql'
#Component({
name: 'List',
apollo: {
listMeta: {
query: listMetaDocument
},
},
})
export default class List extends Vue { }

Vue prerender flickering

I have the following solution now:
<template>
<section id="prod-main">
<prod-preview v-for="prod in products" :id="prod.id" :key="prod.id"/>
</section>
</template>
export default {
...
computed: {
products: function () {
return this.$store.getters['products/getPreview']
}
}
...
}
Vuex store will receive info after some delay from my backend. So at first call it will be empty. Now I want to use vue spa prerender and here I see a flickering.
As I understood it works like:
1. Browser loads HTML with products
2. Execute js that replace products with nothing because the store is empty.
3. After some delay shows it again with backend info.
How can I fix it? I should left prerender for indexing and I can't hardcode the backend reply.
You can use the setting captureAfterTime to wait for your async call to complete, before saving the html of the page.
Other settings are available :
// NOTE: Unless you are relying on asynchronously rendered content,
// such as after an Ajax request, none of these options should be
// necessary. All synchronous scripts are already executed before
// capturing the page content.
// Wait until a specific event is fired on the document.
captureAfterDocumentEvent: 'custom-post-render-event',
// This is how you would trigger this example event:
// document.dispatchEvent(new Event('custom-post-render-event'))
// Wait until a specific element is detected with
// document.querySelector.
captureAfterElementExists: '#content',
// Wait until a number of milliseconds has passed after scripts
// have been executed. It's important to note that this may
// produce unreliable results when relying on network
// communication or other operations with highly variable timing.
captureAfterTime: 5000,
Another issue can be related to how the prerendered HTMl gets hydrated, i've openned an issue on github, but they still haven't addressed it (and are not willing to ?)
https://github.com/chrisvfritz/prerender-spa-plugin/issues/131
The solution is to add data-server-rendered="true" to your vuejs parent node in the prerendered html, like this:
<div id="root" data-server-rendered="true">...
You can use the option postProcessHtml to do so.
I don't know if I understand your problem here but have you tried to add a v-if to avoid flickering:
<template>
<section id="prod-main">
<prod-preview
v-if="products.length > 0"
v-for="prod in products"
:id="prod.id"
:key="prod.id"/>
</section>
</template>