Redundant navigation to current location in VueJs - javascript

I have app with a search function and the initial search works from the homepage. When I try to search again, after the initial results have been fetched and loaded, nothing is returned and I have to go to another page in the app to search again. The search is done through a navbar which appears on every page and the results are returned to a specific view page via an unordered list. I would like to be able to search again after the list page is loaded.
router.js file:
The results are loaded on the "Beers" page.
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: About,
},
{
path: '/beer/:beerId',
name: 'BeerView',
component: BeerView,
props: true
},
{
path: '/beers=?:search',
name: 'Beers',
component: Beers,
props: true
}
]
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
return originalPush.call(this, location).catch(err => err)
}
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes,
duplicateNavigationPolicy: 'ignore'
})
export default router
The page to which the results are loaded, aka "Beers":
<script>
import axios from "axios";
export default {
name: "Beers",
props: ["beers"],
data() {
return {
beersData: this.beers,
};
},
mounted() {
if (this.beers === null || this.beers === undefined) {
axios.get(process.env.VUE_APP_BEER_API_URL).then((data) => {
this.beersData = data.data;
this.$router.replace({ name: "Beers", params: {search: this.$route.params.search} }).catch(()=>{});
});
}
},
methods: {
navigate(id) {
this.$router.push({ name: "BeerView", params: { id: id } });
},
},
};
</script>
The NavBar component:
<script>
import { ref } from "#vue/composition-api";
import axios from "axios";
export default {
name: "Navbar",
props: ["search"],
methods: {
async getBeer(search) {
const res = await axios.get(`${process.env.VUE_APP_BEER_API_URL}?q=${search}`);
this.$router.push({ name: "Beers", params: { beers: res.data} }).catch(()=>{});
},
},
setup({ search }, ) {
const beerQuery = ref(search);
return {
beerQuery,
handleSubmit(event) {
event.preventDefault();
this.getBeer(beerQuery.value);
console.log(beerQuery.value);
},
handleChange(event) {
beerQuery.value = event.target.value;
},
};
},
};
</script>

Related

How to pass dynamic params to name route - Vue JS

I have this LayoutVertical.vue parent component:
<script>
import LayoutVertical from "#core/layouts/layout-vertical/LayoutVertical.vue";
import AppCustomizer from "#core/layouts/components/app-customizer/AppCustomizer.vue";
import { $themeConfig } from "#themeConfig";
import navMenuItems from "#/navigation/vertical";
export default {
components: {
AppCustomizer,
LayoutVertical,
},
data() {
return {
showCustomizer: $themeConfig.layout.customizer,
navMenuItems,
};
},
};
</script>
This navMenuItems property contained:
export default [
{
header: "Projects",
},
{
title: "Import",
route: {
name: "project-import",
params: { token: token },
},
icon: "SettingsIcon",
},
];
And the route for this:
{
path: "/project/import/view/:token",
name: "project-import",
props : true,
component: () =>
import("#/views/apps/projects/project-import/ProjectImport.vue"),
},
Now, I have another compnent called AppNavbarVerticalLayout.vue which is child component of that parent. In this component I have a onChange methods called chooseProject. From this method I can get a token value.
My question is how can I pass this token value to that navMenuItems property's params?
Can I set a params for a specific name route?

How to get Vuejs links to switch between components?

To simplify the demo I'm using fake/pointless API)
I have the routers
const routes = [
{ path: '/', component: Menu},
{path: '/books', component: Index,
props: route => ({ api: `https://jsonplaceholder.typicode.com/posts` })
},
{path: '/videos',component: Index,
props: route => ({ api: `https://jsonplaceholder.typicode.com/todos` })
}
]
const router = new VueRouter({ routes })
const app = new Vue({ router }).$mount('#app')
The Menu component
var Menu = Vue.component('Menu', {
template: `<ul>
<li><router-link to="/">home</router-link></li>
<li><router-link :to="{ name: 'videos'}">videos</router-link></li>
<li><router-link :to="{ name: 'books'}">books</router-link></li>
</ul>`})
This is the problem, it does not switch from videos to books.
Finally the Index component
var BookComponent = {
props: {
api: { type: String }
},
data: function () {
return {
items: null,
}
},
mounted: function(){
this.getItems()
},
methods: {
async getItems() {
fetch(this.api).then(res => res.json())
.then(res =>{
this.items = res;
this.loading = false
})
},
},
template: `
<div class="home">
<p v-for="post in items" :key="post.id">{{post.title}}</p>
</div>
`
}
var Home = Vue.component('Home', BookComponent)
var Index = {
props: {
api: {type: String,},
filterBy: {type: String},
},
template: `
<div>
<Menu />
<div class="mainApp" style="margin-top: 40px!important">
<Home :api=api />
</div>
</div>`,
component: { Menu, Home },
};
It doesn't work on jsfiddle but here's the code anyway jsfiddle
You're calling named routes but you haven't defined any name in your routes so either use the path
<li><router-link to="/videos">videos</router-link></li>
<li><router-link to="/books">books</router-link></li>
or name your routes
const routes = [
{ path: '/', component: Menu},
{path: '/books', component: Index, name: 'books',
props: route => ({ api: `https://jsonplaceholder.typicode.com/posts` })
},
{path: '/videos',component: Index, name: 'videos',
props: route => ({ api: `https://jsonplaceholder.typicode.com/todos` })
}
]
To fix navigation - replace objects in router-link attribute to with to='/videos' and to='/books'
To fix render error, add v-if to post items loop, like this:
<div class="home">
<p v-if="items" v-for="post in items" :key="post.id">{{post.title}}</p>
</div>
Here is official docs for vue-router
It seems that I just needed to watch for the route changes and trigger the fetch method again.
watch: {
// call again the method if the route changes
'$route': 'getItems'
}

Load different components for different base href with same path name in Angular

I've the following path's in my angular application, For home path base href is set to /home/ and for create /create/
http://localhost:3000/home/account
https://localhost:3000/create/account
Dynamic Route Config path,
export const AppRoute = {
'homeRoutes': [
{ path: 'account', loadChildren: () => import('#home').then(m => m.HomeModule) },
{ path: 'test', loadChildren: () => import('#test').then(m => m.TestModule) },
{ path: '**', component: PageNotFoundComponent }
],
'createRoutes': [
{ path: 'account', loadChildren: () => import('#create').then(m => m.CreateModule) },
{ path: 'login-test', loadChildren: () => import('#logintest').then(m => m.LoginTestModule) },
{ path: '**', component: PageNotFoundComponent }
]
};
In AppComponent.ts
resetRouter () {
const baseHref = this.platformLocation.getBaseHrefFromDOM();
if (baseHref.match('/home/')) {
this.router.resetConfig(routes.homeRoutes);
}
if (baseHref.match('/create/')) {
this.router.resetConfig(routes.createRoutes);
}
}
In Routing Module
const routes = [
{ path: 'home/account', loadChildren: () => import('#home').then(m => m.HomeModule) },
{ path: 'create/account', loadChildren: () => import('#home').then(m => m.HomeModule) },
{ path: 'test', loadChildren: () => import('#test').then(m => m.TestModule) },
{ path: 'login-test', loadChildren: () => import('#logintest').then(m => m.LoginTestModule) },
{ path: 'page-not-found', component: PageNotFoundComponent }
];
So now the problem is,
My app now is now accessible only in http://localhost:3000/home/home/account which is not expected.
Also, If as access to create routes f.e http://localhost:3000/home/test works , but actual expectation is to throw error page.
Please help me configure these routes.
I would change your approach. Since the routing config is just a constant you can duplicate your route config for both by unpacking a mapped array into the routes const (see this answer):
const routes = [
...['home', 'create'].map(path => ({
path: path,
component: BaseComponent,
loadChildren: () => import('#home').then(m => m.HomeModule)
})),
{ path: 'test', loadChildren: () => import('#test').then(m => m.TestModule) },
{ path: 'login-test', loadChildren: () => import('#logintest').then(m => m.LoginTestModule) },
{ path: 'page-not-found', component: PageNotFoundComponent }
];
Then inside of your BaseComponent.ts you use the ActivatedRoute API to figure out whether it's home or create:
public BaseComponent {
public slug: string = undefined;
construct(private route: ActivatedRoute) { }
ngOnInit() {
this.slug = this.route.snapshot.url[0].path;
}
}
In the html use *ngIf to display either the home or create component:
<app-home *ngIf="slug == 'home'"></app-home>
<app-create *ngIf="slug == 'create'"></app-create>

Change route name and component if user is logged in

I have a functioning login component in my nativescript-vue that utilizes the RadSideDrawer.
All I want to do is change the Login route to Logout and I can't seem to figure out how to achieve that. Ternary operators wouldn't fully work.
I also tried declaring a new empty array and put my menu array in it and manipulate that instead of the original data. This did not work either.
I would need to close the app and open it again for the menu item to change, it wasn't reactive.
First of all, here my app.js
Vue.prototype.$routes = routes
new Vue({
store,
render (h) {
return h(
App,
[
h(DrawerContent, { slot: 'drawerContent' }),
//h(store.getters.loggedIn ? routes.Home : routes.Login, { slot: 'mainContent' })
h(routes.Home, { slot: 'mainContent' })
]
)
}
}).$start()
And here's my router in router/index.js where I'm declaring the routers.
import Home from "../components/Home";
import Browse from "../components/Browse";
import Featured from "../components/Featured";
import Search from "../components/Search";
import Settings from "../components/Settings";
import Tasks from "../components/Tasks";
import Login from "../components/Login";
import Logout from "../components/Logout";
const routes = {
Home,
Browse,
Featured,
Search,
Settings,
Tasks,
Login,
Logout
}
export default routes
I am also using a $store with a bunch of getters, setters, mutations and actions and one of those getters retrieves if the user is logged in
export const store = new Vuex.Store({
state: {
token: LS.getItem('access_token') || null,
filter: 'all',
todos: [],
msg: ''
},
getters: {
loggedIn(state) {
return state.token !== null
},
}
}
And since I'm using the Drawer navigation, here's DrawerContent.vue
<template lang="html">
<GridLayout rows="auto, *" class="sidedrawer sidedrawer-left">
<StackLayout row="0" class="sidedrawer-header">
<Label class="sidedrawer-header-image fa" text.decode=""></Label>
<Label class="sidedrawer-header-brand" text="User Name"></Label>
<Label class="footnote" text="username#mail.com"></Label>
</StackLayout>
<ScrollView row="1" class="sidedrawer-content">
<StackLayout>
<GridLayout
columns="auto, *"
:class="'sidedrawer-list-item' + (selectedPage === page.name ? ' selected': '')"
v-for="(page, i) in pages"
:key="i"
#tap="goToPage(page.component)">
<Label col="0" :text="page.icon" class="fa"></Label>
<Label col="1" :text="page.name" class="p-r-10"></Label>
</GridLayout>
</StackLayout>
</ScrollView>
</GridLayout>
</template>
<script>
import * as utils from "~/shared/utils";
import SelectedPageService from "~/shared/selected-page-service";
import Login from "./Login";
import Featured from "./Featured";
export default {
data () {
return {
selectedPage: "",
pages: [
{
path: '/',
name: 'Home',
icon: "\uf015",
component: this.$routes.Home
},
{
path: '/browse',
name: 'Browse',
icon: '\uf25a',
component: this.$routes.Browse,
meta : {
requiresAuth: true
}
},
{
path: '/featured',
name: 'Featured',
icon: '\uf005',
component: this.$routes.Featured
},
{
path: '/search',
name: 'Search',
icon: '\uf002',
component: this.$routes.Search
},
{
path: '/settings',
name: 'Settings',
icon: '\uf013',
component: this.$routes.Settings
},
{
path: '/tasks',
name: 'Tasks',
icon: '\uf0ae',
component: this.$routes.Tasks
},
{
path: '/login',
name: 'Login',
icon: '\uf007',
component: this.$routes.Login
}
]
};
},
mounted() {
SelectedPageService.getInstance().selectedPage$
.subscribe((selectedPage) => this.selectedPage = selectedPage);
},
methods: {
goToPage (pageComponent) {
this.$navigateTo(pageComponent, {
clearHistory: true
});
utils.closeDrawer();
},
},
computed: {
loggedIn() {
return this.$store.getters.loggedIn
},
}
};
</script>
As you can see, here is where the routes are actually defined in a pages array and then looped over in the GridLayout above.
The problem with this is that it leaves little room for wiggle as I have to deal with data coming from the loop itself if(page.name === 'Login') { page.name = 'Logout' }
This was my initial attempt, using the v-if directive. But I quickly abandoned it as the statement would be too long and would be kinda of messy.
I also tried to remove the loop and just hard-code the entire menu, but I was unable to access this.$routes.Home in the markup.
At this point I'm really lost. I'm open to any suggestion you might have.
Make the pages property a getter so you can push into the array easily:
computed: {
pages: function() {
const pages = [{
path: '/',
name: 'Home',
icon: "\uf015",
component: this.$routes.Home
},
{
path: '/browse',
name: 'Browse',
icon: '\uf25a',
component: this.$routes.Browse,
meta: {
requiresAuth: true
}
},
{
path: '/featured',
name: 'Featured',
icon: '\uf005',
component: this.$routes.Featured
},
{
path: '/search',
name: 'Search',
icon: '\uf002',
component: this.$routes.Search
},
{
path: '/settings',
name: 'Settings',
icon: '\uf013',
component: this.$routes.Settings
},
{
path: '/tasks',
name: 'Tasks',
icon: '\uf0ae',
component: this.$routes.Tasks
},
]
if (!!this.$store.getters['loggedIn']) {
pages.push({
path: '/logout',
name: 'Logout',
icon: '....',
component: this.$routes.Logout
})
} else {
pages.push({
path: '/login',
name: 'Login',
icon: '\uf007',
component: this.$routes.Login
})
}
return pages
}
}

Update translation without page refresh (i18next with react)

My _nav.js file:
import i18n from '../../services/Translator';
export default {
items: [
{
name: i18n.t('DASHBOARD'),
url: '/dashboard',
icon: 'icon-speedometer',
},
{
name: i18n.t('SCHEDULE'),
url: '/schedule',
icon: 'icon-calendar',
},
{
name: i18n.t('USERS'),
url: '/users',
icon: 'icon-user',
},
{
name: i18n.t('LEASING_COMPANY'),
url: '/company',
icon: 'icon-organization',
},
],
};
My component:
import { translate } from 'react-i18next';
import nav from '../Sidebar/_nav';
/...
render() {
const test = nav.items.map((item) => {
return <li key={item.url}>{item.name}</li>;
});
return (
<ul>{test}</ul>
);
}
The problem is I don't get my text translated when I change language. My browser need to be refreshed to apply translation. Anyone know how to get translation done without page refresh?
Edit: This is my Translator service:
import i18n from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import en from '../../lang/en';
import vn from '../../lang/vn';
import env from '../../config/env';
i18n
.use(LanguageDetector)
.init({
// we init with resources
resources: {
en,
vn,
},
fallbackLng: env.defaultLanguage,
// have a common namespace used around the full app
ns: ['translations'],
defaultNS: 'translations',
keySeparator: false, // we use content as keys
react: {
wait: true,
},
});
export default i18n;
I also put my change language button on the Header component in my page.
Can't work like this...you assign translations to objects in the array inside _nav.js
Those will be strings no way any code will ever update those values you will need to regenerate those on language change or:
import i18n from '../../services/Translator';
export default {
items: [
{
name: 'DASHBOARD',
url: '/dashboard',
icon: 'icon-speedometer',
},
{
name: 'SCHEDULE',
url: '/schedule',
icon: 'icon-calendar',
},
{
name: 'USERS',
url: '/users',
icon: 'icon-user',
},
{
name: 'LEASING_COMPANY',
url: '/company',
icon: 'icon-organization',
},
],
};
and
import { translate } from 'react-i18next';
import nav from '../Sidebar/_nav';
/...
render() {
const test = nav.items.map((item) => {
return <li key={item.url}>{t(item.name)}</li>;
});
return (
<ul>{test}</ul>
);
}

Categories