Update translation without page refresh (i18next with react) - javascript

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>
);
}

Related

How to store React component in object

I have an object of all apps:
export const allApps: appInterface[] = [
{
id: uuidv4(),
name: "Minesweeper",
icon: minesweeperIcon,
disabled: false,
},
];
I want to add component property to each object like this:
export const allApps: appInterface[] = [
{
id: uuidv4(),
name: "Minesweeper",
icon: minesweeperIcon,
disabled: false,
component: Minesweeper, //It is imported as import Minesweeper from ../Components/Minesweeper";
},
];
And then I want to display all components in App.js:
allApps.forEach((app)=>{
<div>{app.component}<div> // for eg: <div><Minesweeper/></div>
});
Is this possible to do?
Try as below code it should work.
allApps.map((app)=>{
const { component: Component } = app;
return <Component />;
});

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?

Redundant navigation to current location in VueJs

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>

I am having an issue with integrating an existing react component I found, into my app. "Attempted import error:"

My goal is to bring an existing react component (below) that I found on the web, into my app to use. It is an animated navigation bar.
Here is the error message I receive after trying to compile the below code (snippets 2 and 3 below):
Failed to compile.
./src/components/Navbar.js
Attempted import error: 'react-responsive-animate-navbar' does not contain a default export (imported as 'ReactNavbar').
Maybe there is an issue with the library I installed? The creator had made this component 7 months ago, here - https://www.npmjs.com/package/react-responsive-animate-navbar.
Regardless, please see below for details on 1. the component I am trying to bring in, 2. the component in my code, refactored slightly and 3. the file I am importing this component into.
This is the component I found on the web. It renders an awesome navbar I would like to bring into my app. HOWEVER, I have never used the "class ... extends compoent" syntax. I have only used the "export default function name(){return()}" style.
npm install --save react-responsive-animate-navbar
import React from "react";
import ReactNavbar from "react-responsive-animate-navbar";
class Example extends Component {
render() {
return (
<ReactNavbar
color="rgb(25, 25, 25)"
logo="https://svgshare.com/i/KHh.svg"
menu={[
{ name: "HOME", to: "/" },
{ name: "ARTICLES", to: "/articles" },
{ name: "ABOUT ME", to: "/about" },
{ name: "CONTACT", to: "/contact" },
]}
social={[
{
name: "Linkedin",
url: "https://www.linkedin.com/in/nazeh-taha/",
icon: ["fab", "linkedin-in"],
},
{
name: "Facebook",
url: "https://www.facebook.com/nazeh200/",
icon: ["fab", "facebook-f"],
},
{
name: "Instagram",
url: "https://www.instagram.com/nazeh_taha/",
icon: ["fab", "instagram"],
},
{
name: "Twitter",
url: "http://nazehtaha.herokuapp.com/",
icon: ["fab", "twitter"],
},
]}
/>
);
}
}
Here is the component since I have brought it into my app and slightly refactored it to fit the react syntax I have been using.
./src/components/Navbar.js
import React from "react";
import ReactNavbar from "react-responsive-animate-navbar";
export default function Navbar({ setSignedIn }) {
return (
<ReactNavbar
color="rgb(25, 25, 25)"
logo="https://svgshare.com/i/KHh.svg"
menu={[
{ name: "HOME", to: "/Explore" },
{ name: "ARTICLES", to: "/articles" },
{ name: "My Profile", to: "/profile" },
{ name: "CONTACT", to: "/contact" },
]}
social={[
{
name: "Linkedin",
url: "https://www.linkedin.com/in/nazeh-taha/",
icon: ["fab", "linkedin-in"],
},
{
name: "Facebook",
url: "https://www.facebook.com/nazeh200/",
icon: ["fab", "facebook-f"],
},
{
name: "Instagram",
url: "https://www.instagram.com/nazeh_taha/",
icon: ["fab", "instagram"],
},
{
name: "Twitter",
url: "http://nazehtaha.herokuapp.com/",
icon: ["fab", "twitter"],
},
]}
/>
);
}
And finally, here is where I am attempting to import this component for it to render onto an actual page.
./src/Pages/IntroPage
import React from "react";
import Navbar from "../components/Navbar";
import Categories from "../components/Categories";
export default function ChooseACategory({ setSignedIn }) {
return (
<div>
<Navbar setSignedIn={setSignedIn} />
<Categories />
</div>
);
}
I suspect the issue is with my refactoring from the "class ... extends component" syntax to the "export default function name" syntax (which I am more familiar with). I understand there is not a big difference between the two, but I would rather keep my code for this project uniform. Any help is welcome!
There was nothing wrong with you refactor, because you did not use any states. I installed that package, with nothing changed. The same wrong message as yours appeared when I started it. I suspect this package itself is not runnable.
Looking at the source for that package, it does not contain a default export. It has:
export const ReactNavbar without the default.
Try importing the named component instead of as a default:
import { ReactNavbar } from instead of import ReactNavbar from
For my situation, I found a workaround solution, it not renders and works properly using the highlighted edit in the screenshot below. Thanks!
import React from "react";
import * as ReactNavbar from "react-responsive-animate-navbar";
import Logo from "../Assets/Media/ToolioLogoSmall.png"
export default function Navbar({ setSignedIn }) {
console.log(ReactNavbar.ReactNavbar) //Edited, fixed issue.
return (
<div style={style.background}>
<ReactNavbar.ReactNavbar style={style.background}
color="rgb(25, 25, 25)"
logo={Logo}
menu={[
{ name: "HOME", to: "/Explore" },
{ name: "ARTICLES", to: "/articles" },
{ name: "My Profile", to: "/profile" },
{ name: "CONTACT", to: "/contact" },
]}
social={[
{
name: "Linkedin",
url: "https://www.linkedin.com/",
icon: ["fab", "linkedin-in"],
},
{
name: "Facebook",
url: "https://www.facebook.com/",
icon: ["fab", "facebook-f"],
},
{
name: "Instagram",
url: "https://www.instagram.com/",
icon: ["fab", "instagram"],
},
{
name: "Twitter",
url: "http://www.twitter.com/",
icon: ["fab", "twitter"],
},
]}
/>
</div>
);
}

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
}
}

Categories