Whats the simplest and proper way to route in a template - vue.js

Instead of showing data from a component, I'm currently routing a user to login at /account if they are not authorized, by doing this:
<template>
<q-card v-if="authorized">
<q-card-section>
<DataGrid/>
</q-card-section>
</q-card>
<span v-else>
{{ this.$router.push('/account') }}
</span>
</template>
It's simple and works, but I'm not sure its really correct because although it pushes the user to where I want them to be, the console gets this error:
uncaught exception: undefined
(I'm currently on Quasar v1.9.14)
Basically I want to show the data if the user is authorized or redirect if they are not authorized, or become unauthorized later.

first of all, you do not need to use this in the template.
If you want to route them based on the authorized value, you could probably use a watcher.
Alternatively, I would probably do something in mounted which checks if the user can be there. E.g.
async mounted ()
{
const authorized = await fetch("something")
if (!authorized)
{
this.$router.push('/account')
}
}

Its simpler than I thought. I believe the answer is events. The span can simply change to:
<span v-else #load="$router.push('/account')"/>
<span v-else :class="authorized ? '' : $router.push('/account')"/>
Its even simpler because its a one liner
It will work even after mounted() has already fired and authorized becomes false
Actually, I now can delete similar logic in mounted() (DRY principle)
EDITS:
After proper testing I found it actually does not work with my first #load event example.

Related

Nuxt3 useFetch only retrieving data occasionally

I'm a little bit confused with Nuxt 3 and the lifecycle of when it gets data. I understand that it's a universal rendering process, but I'm using Strapi 4 to manage content in my Nuxt 3 project and only occasionally do I retrieve the data via useFetch. The API route from Strapi never goes down so I'm probably just doing something wrong.
Here is my Vue file in Nuxt:
<script setup lang="ts">
const {data: works, pending, error} = await useFetch("http://localhost:1337/api/works", {
params: {
populate: "*"
}
});
</script>
<template>
<div>
<div v-for="work in works">
... do something
</div>
</div>
</template>
I'm not sure how to get the content when the page loads. When I log the error returned, it's just true. But it's only sometimes. The content will load once, and then as soon as I refresh the page, it goes back to having an error. So I'm thinking something is getting cached maybe client-side? I'm really not sure what to do next.
Try passing this option to useFetch: initialCache: false. See more

undefined data error on page reload Nuxt.js

I'm currently developing a universal app using Nuxt.js, the data on most of the pages is retrieved from an API using a fetch hook as well as vuex store. I started noticing errors on page reload/refresh and sometimes when I visit a page from the navbar. The page error is:
TypeError: Cannot read property 'data' of undefined
where data is an object retrieved from an API. I have searched around the internet for this and found it has something to do with data not being loaded or page rendering whilst the data is not fully retrieved. i have found a work around by using a v-if on my template to check if the data is set then display the contents. my question is if there is a way to achieve this, i have tried using the async/await keywords but it doesn't help and i feel like the v-if method is not the best way to handle it.
edit: solved by using $fetchState.pending when using fetch()
If in your template you display right away the data you retrieve from the API, then indeed using the v-if is the right way to do.
If you are using the new fetch() hook, then in your template you can use $fetchState.pending to check if the loading has finished, for example:
<div v-if="$fetchState.pending">
<p> Content is loading... </p>
</div>
<div v-else>
<p> Content has loaded! {{data}}</p>
</div>
<script>
export default{
data(){
return{
data: null
}
}
async fetch(){
this.data = await getSomeAPI
}
}
</script>

Vue rendering only part of my App.vue after a login

I am working on a Vue.js application that I am almost done, one major bug left. The bug/issue is that when you go to /login and login to the site you get redirected via a router push (tried replace too) and when this happens I want to render the whole dashboard. Currently since in my App.vue file the router view is a different part it only renders the dashboard info part and not my header or sidebar.
Pretty much imagine a dashboard without a header or sidebar. That's what's rendering. I'd be okay if I could do something like F5 does because then it all would load correctly though taking up to 2 seconds longer on login which is okay by me.
My App.vue file template code
<template>
<div class="fade page-sidebar-fixed page-header-fixed show page-container" v-if="!pageOptions.pageEmpty" v-bind:class="{
'page-sidebar-minified': pageOptions.pageSidebarMinified,
'page-content-full-height': pageOptions.pageContentFullHeight,
'page-with-top-menu': pageOptions.pageWithTopMenu,
'page-sidebar-toggled': pageOptions.pageMobileSidebarToggled,
'has-scroll': pageOptions.pageBodyScrollTop
}">
<Header />
<Sidebar v-if="!pageOptions.pageWithoutSidebar" />
<div id="content" class="content" v-bind:class="{ 'content-full-width': pageOptions.pageContentFullWidth, 'content-inverse-mode': pageOptions.pageContentInverseMode }">
<router-view></router-view>
</div>
</div>
<div v-else>
<router-view></router-view>
</div>
</template>
Looks like I have resolved my issue, it comes from vue-router and how I am doing that if statement in my template code. So in that code I am checking a boolean value then choosing which view to render. So I had though on all of my auth pages I set the value correctly on exit. Turns out not...
This was in my Login.vue file, idea was to have on an exit of the route that it would change the boolean to false which would let me render it right. This was something I did initally but had forgotten about till about 20 minutes ago.
Upon checking this I found the value was not being changed for some reason. So as a work around in the created part of my Dashboard.vue file I set the value to false explicitly
Login.vue
beforeRouteLeave (to, from, next) {
PageOptions.pageEmpty = false;
next();
},
Dashboard.vue
created() {
PageOptions.pageEmpty = false;
...
}
The main idea is to have several base pages each one of them is operate with its own set of internal views.
You have to redirect user to another view, which is the one and only active view and this view can contains sidebar header and main part that also contains router-view, and then! you load any needed components in it.
You have to have something like that:
App component is only contains router view tag and any other pages are load into this.
The routes structure then looks like that:
As you can see, there are two base views load in App view. And then the base view can has a lot of children. The level of nested routes is up to you. Here is the contents of my app Home view:
And the MainContent component which is contains router view only:
The good example of project structure is the one generated with vue-cli. You can use it to simplify dev process with a lot of benefits and good practice solutions.

Where is the state of a Electron-Vue application stored?

How and, more to the point, where is state data persisted between sessions?
I followed this tutorial (https://alligator.io/vuejs/vue-electron/) to create a simple ToDo App using Vue.js in Electron.
After filling in some blanks everything works. However, I'm unable to find where the application state is stored. For example, in Dev mode (npm run dev), if I add some ToDo items and then close the App (and even restart VS Code) the ToDo data is persisted when the App is restarted.
I'm using Win10 and, among other things, have tried searching for files changed within the last 30 minutes (datemodified:‎23/‎09/‎2019 12:30..13:00) but without luck. I've looked in my profile, the App directory, and searched google for clues.
[Edit]
I think it has something to do with this import { createPersistedState, createSharedMutations } from 'vuex-electron' (located in src\renderer\store\index.js) but not much closer to understanding where the state is stored. Yep - I'm pretty new to this.
For what it's worth, if you want to follow this tutorial:
Issue 1.
Now that we know how the Vue application is started, let’s take a look at the defined routes within our application. Head over to src/router/index.js:
should be: src/renderer/router/index.js:
Issue 2. In src/renderer/components/LandingPage.vue
<div class="todos">
<ul>
<li
class="todo-item"
v-for="todo in todos"
:key="todo.id"
#click="completeTodo(todo)"></li>
</ul>
</div>
should be:
<div class="todos">
<ul>
<li
class="todo-item"
v-for="todo in todos"
:key="todo.id"
#click="completeTodo(todo)">{{todo.name}}</li> // {{todo.name}}
</ul>
</div>
Using 'createPersistedState' option of 'vuex-electon' saves in json format in 'AppData' path.
uerName/AppData/Roaming/Electron/vuex.json
Remove 'createPersistedState' to disable saving.
import { createSharedMutations } from 'vuex-electron'
export default new Vuex.Store({
modules,
plugins: [
createSharedMutations()
],
strict: process.env.NODE_ENV !== 'production'
})
see a https://github.com/vue-electron/vuex-electron

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>