Error in rendering vue.js file using view instance new Vue() - vue.js

I am working with vue.js, while rendering vue page i am getting below
error "Cannot GET /KForm"
Below are my code in main.js
import * as componentBase from "#app/app_start"
import Form from "#views/Form/Form.vue"
import KForm from "#views/KForm/KForm"
const NotFound = { template: '<p>Page not found</p>' }
const routes = {
'/': Form,
'/recap': Recap,
'/KForm': KForm
}
const app = new Vue({
...componentBase,
data: {
currentRoute: window.location.pathname
},
computed: {
ViewComponent() {
return routes[this.currentRoute] || NotFound
}
},
render: h => h(this.ViewComponent)
}).$mount("#app")

I'm not sure what your webpack config is, but shouldn't the KForm import be this:
import KForm from "#views/KForm/KForm.vue"
^^^^
currentRoute is set to window.location.pathname one time only when the component is instantiated. When you click a link (or navigate directly from the browser's address bar) to, say /KForm, the window location changes and the browser tries to fetch the webpage at that new address just like in a traditional non-SPA webpage. This will fail unless the server responds to that URL.
To prevent the browser from doing this, you'll have to intercept <a> clicks and use the history API to change the window location without reloading the page, then change currentRoute accordingly.
Or better yet, just use vue-router which does all this for you. See this for example server configurations for HTML5 history mode.

Related

Redirect with page reload Vue JS Router

I have an app with a Login.vue and Home.vue files. Because I converted an admin HTML website to a vue 3 app, my javascript only works with page reload. When creating the app I selected add router for SPA maybe I shouldn't have. Up to this point, the views are working except when I redirect from login to home without reloading. Since it is not reloading, my navbar or any JS-dependent functions won't work. how do I redirect from login to home with page reload? Currently, I have the below code but still not working.
this.$router.push({
path: "/admin/home",
reload: true
});
You can use this.$router.go() with empty arguments to reload the page. In combination with this.$router.push({ path: '/admin/home' }) you can achieve it without using vanilla JS features.
<template>
<button #click="redirectReload">Redirect & Reload</button>
</template>
<script>
export default {
methods: {
redirectReload() {
this.$router
.push({ path: '/expedition' })
.then(() => { this.$router.go() })
}
}
}
</script>
Notice how I used .then after $router.push(). Without then the page reloads too quickly, leaving no time for the route to change.
As a bonus, it lets you use all the features of $router.push (for example using arguments like { name: 'home' }.
Vue Router not reload page when you navigate to new URL.
You can try this code for you issue:
const url = new URL('/admin/home', window.location.origin)
window.location.href = url.toString()
Hope this help

How to get SPA navigation working with external framework that uses innerHTML for content

In my Vue.js app, I am using a bootstrap-based framework that generates the html for my header and a menu nav with links, which is then inserted into the page by assigning innerHTML to a mount point.
But when I use the generated content to navigate, the entire page reloads since the links aren't using <router-link>.
One attempt at a fix:
In the Vue app, I assigned a method called goto on the window object that would perform programmatic router navigation.
I was then able to pass javascript:window.goto("myPageName"); as the href attribute, but this comes with many undesirable side-effects.
How can I cleanly make the links navigate without reloading the page?
(The framework needs jQuery as a dependency, so that is able to be used in a solution.)
I was able to use a MutationObserver that watches for subtree changes and adds a custom click handler when it detects the links being added via .innerHTML.
With this method, I specify vue-goto:myPageName as the href attribute, and then the handler will take care of making it an SPA link.
import { router } from "#/router";
import { store } from "#/store";
export const attrib = "vue-goto";
export const prefix = attrib + ":";
function onChange() {
// find all links matching our custom prefix to which we have not yet added our custom handler
const links = window.$(`a[href^='${prefix}']`).not(`[${attrib}]`);
// add custom attribute for us to grab later
links.attr(attrib, function() {
// jQuery doesn't like arrow functions
return window
.$(this)
.attr("href")
.substr(prefix.length)
.trim();
});
// Update href on the link to one that makes sense
links.attr("href", function() {
return router.resolve({
name: window.$(this).attr(attrib), // grab attribute we saved earlier
params: { lang: store.state.language }, // in our case, our pages are qualified by a language parameter
}).href;
});
// Override default click navigation behaviour to use vue-router programmatic navigation
links.click(function(e) {
e.preventDefault(); // prevent default click
const routeName = window.$(this).attr(attrib);
const goto = {
name: routeName,
lang: store.state.language,
};
router.push(goto).catch(ex => {
// add catch here so navigation promise errors aren't lost to the void causing headaches later
// eslint-disable-next-line no-console
console.error(
`Error occurred during navigation from injected [${prefix}${routeName}] link`,
"\n",
ex,
);
});
});
}
let observer;
export function init() {
if (observer) observer.unobserve(document.body);
observer = new MutationObserver(onChange);
observer.observe(document.body, {
characterData: false,
childList: true,
subtree: true, // important, we want to see all changes not just at toplevel
attributes: false,
});
}
init();

How can I add vue-router link as ag-grid-vue column?

ag-grid-vue documentation from ag-grid website clearly says:
You can provide Vue Router links within the Grid, but you need to
ensure that you provide a Router to the Grid Component being created.
with sample code:
// create a new VueRouter, or make the "root" Router available
import VueRouter from "vue-router";
const router = new VueRouter();
// pass a valid Router object to the Vue grid components to be used within the grid
components: {
'ag-grid-vue': AgGridVue,
'link-component': {
router,
template: '<router-link to="/master-detail">Jump to Master/Detail</router-link>'
}
},
// You can now use Vue Router links within you Vue Components within the Grid
{
headerName: "Link Example",
cellRendererFramework: 'link-component',
width: 200
}
What's missing here is how to make the "root" Router available. I've been looking into various sources and see many people have the same problem, but none got a clear answer.
https://github.com/ag-grid/ag-grid-vue/issues/1
https://github.com/ag-grid/ag-grid-vue/issues/23
https://github.com/ag-grid/ag-grid-vue-example/issues/3
https://forum.vuejs.org/t/vue-cant-find-a-simple-inline-component-in-ag-grid-vue/21788/10
Does ag-grid-vue still work with vue-router, then how, or is this just outdated documentation? Some people claim it worked for them so I assume it worked at one point.
I am not looking for cool answer at this point. I just want to know if it is possible. I tried passing router using window or created() and none worked so far.
Thank you!
the approach suggested by #thirtydot works well. The only downside was the user cannot right-click, but I found you can just define href link. So when you left-click, event listener makes use of router. When you right-click and open in new tab, browser takes href link.
You still need to make your root router available. Below code sample assumes you have the code inside the vue-router-aware Vue component that consumes ag-grid, hence this.$router points to the root router.
{
headerName: 'ID',
field: 'id',
cellRenderer: (params) => {
const route = {
name: "route-name",
params: { id: params.value }
};
const link = document.createElement("a");
link.href = this.$router.resolve(route).href;
link.innerText = params.value;
link.addEventListener("click", e => {
e.preventDefault();
this.$router.push(route);
});
return link;
}
}

Aurelia: change navigation in app.js from view

I have an Aurelia project with navigation in app.html and app.js. The project includes a home page that has a different style to it, including navigation that is different than the non-home page views.
I would like to turn off navigation for the home view so I tried setting a variable (showMenu) to toggle the visibility. In fact, I am able to use jQuery to do this, but I wonder if there is an Aurelia way of doing it. If I set this.showMenu to true it shows the menu container, and false hides it. Like this for example:
app.html
<div class="container" if.bind="showMenu">
app.js
constructor(router){
this.router = router;
this.showMenu = true;
...other things
}
What I would like to do is set showMenu to false from home.js. I tried this (among 20 or so other attempts), but it does not work.
home.js
activate() {
this.showMenu = false;
}
Is there a way through $parent or some other means to hide the menu in app.html using a view model?
EDIT
This works but it feels a little like a hack.
home.js
import {inject} from 'aurelia-framework';
import {Router} from 'aurelia-router';
#inject(Router)
export class Home {
constructor(router) {
this.router = router;
}
attached(){
$("#navbarMenu").hide();
this.router.refreshNavigation();
}
}
You should be able to use router to achieve that. Since this is required for one page only, you can have something like this assuming your route name is home (or you could use other properties of RouteConfig that you have set in configureRouter):
<div class="container" if.bind="router.currentInstruction.config.name !== 'home'">
I approach this problem by using separate shells. By default Aurelia will start your app with app.js (or ts). But you can change that default and also use the same command to redirect to a new shell after authentication.
In your main.ts (or .js) you will have a line to start your aurelia app:
aurelia.start().then(() => aurelia.setRoot());
This line is telling aurelia to start and to set the root view model for your app, when aurelia.setRoot() has no value given it defaults to app.ts (or .js).
So I create a landing for my app where I can display with the page and styles I wish completely separately from the main app, including a limited router and navigation.
export function configure(aurelia: Aurelia) {
aurelia.use
.standardConfiguration()
if (environment.debug) {
aurelia.use.developmentLogging();
}
if (environment.testing) {
aurelia.use.plugin('aurelia-testing');
}
aurelia.start().then(() => aurelia.setRoot('authPage'));
}
authPage.ts is my usual app.ts with a router configuration but it will only have the authPage configured in it and perhaps one or two other welcome pages.
The authPage takes care of authentication and obtaining appropriate tokens. I use a 3rd party for authentication services so all I have on this page is a link. Either way after successful authentication is confirmed you now just want to redirect to an alternative aurelia shell.
#autoinject
export class AuthPage {
private app : Aurelia;
private router : Router;
constructor(router : Router, app: Aurelia) {
this.app = app;
this.router = router;
}
authenticate {
//some kind of authentication procedure...
if(authenticationSuccess) {
this.router.navigate('/', { replace: true, trigger: false});
this.router.reset();
this.router.("authenticatedApp");
}
}
The lines this.router.navigate('/', { replace: true, trigger: false}); and this.router.reset(); are provided to deal with issues mentioned here and also on SO here. The shell switch line this.router.("authenticatedApp"); doesn't work for me without the other two.
My authenticatedApp configures a full router and navigation menu for the user in just the same way as you would normally do with app.ts but now separated into its own shell.
Of course there is nothing to prevent someone linking straight to authenticatedApp but at this point there is no data displayed without an api call which all require an access token to be presented.
This is a useful link on building an Aurelia app with multiple shells for authentication.
The end result is a separated landing pages and application pages which can have different styles and different navigation.On logout you can do the same thing in reverse to reload the auth page.

How to use vuejs global event to notify after redirection

I have created a global vuejs event named EvenBus in a file called app.js as follows:
window.EventBus = new Vue();
After deleting a person from a database, I redirect to the home page and I emit an event called deleted like so:
import {app} from '../app';
export default {
methods: {
deleteMe: function () {
axios.delete('/api/persons/' + this.person.id)
.then(response => {
window.location.href = '/home';
EventBus.$emit('deleted', {
notification_msg: 'This person has been successfully deleted.',
});
})
},
}
on the page home, I have inserted a tag <notify></notify> linked to a component called notify.vue.
In this component, I added the following script:
import {app} from '../app';
export default {
/* ... */
mounted() {
let that = this;
console.log(EventBus);
EventBus.$on('deleted', function(data){
alert('person deleted!!' + data.notification_msg);
that.msg_text = data.notification_msg;
});
},
}
When the delete happens, I get successfully redirected to the home page, but nothing happens there. The alert('person deleted!!...') never shows up.
The code is executed with no error.
Am I missing something to make my component listen to the emitted event?
EDIT
The line console.log(EvenBus); written in the notify.vue file shows that there is a event called 'deleted' (c.f. printscreen below)
The issue here is that when this line of code is executed,
window.location.href = '/home';
the page you are on is replaced with the /home page. So, it's possible it never even gets to the line that emits the event and if it does, the object that listens to the event is gone when the page is loaded.
You might want to look into using VueRouter so that the page isn't destroyed. Other than that, possibly tell the server when you are redirecting that it needs to show the notification when the page is loaded.