I have added navigation to the drawer, everything works fine except the case when the link is the same as the page is. The drawer left open after clicking on the link and it isn't obvious for the visitor that the page is the current page.
I have tried to do something like this #click.stop="(this.router.path != '/' ?
this.router.push('/') : drawer = !drawer)" Vue doesn't rapport any mistake and the code doesn't work.
Where am I wrong?
The drawer data key is looking for a boolean, if it's truthy the navigation drawer will show. So, you can add #click="drawer = false" to your menu links, and it will close the draw when any link is clicked.
Example in the docs: https://vuetifyjs.com/components/navigation-drawers#example-6
I handled this by making the drawer in my app a child of the route that uses it. The isOpen property is managed in the parent. I pass isOpen as a prop to the drawer and emit open and close events as appropriate.
Oh, I also found that timeouts are necessary to ensure the open / close animations work correctly. Someone please let me know if you found a better way to handle animations as this feels a little wonky.
I handle a few other things, like right/left justify and a return route, but ignore the noise if it isn't helpful.
Here's a parent loading the component
<my-drawer
:iconName="'my_icon'"
:isOpen="drawerIsOpen"
:justify="'right'"
:returnRoute="'home'"
#close="drawerIsOpen = false"
#open="drawerIsOpen = true"
>
// ...
</my-drawer>
Here are the methods from within the drawer component:
data() {
return {
closeDelay: 500,
width: 0,
};
},
methods: {
closeBtnClick() {
this.$emit('close');
setTimeout(() => { this.$router.push(this.returnRoute); }, this.closeDelay);
},
mounted() {
setTimeout(() => { this.$emit('open'); }, this.closeDelay);
},
Related
I've thoroughly read the documentation on passing params between screens with React Navigation: https://reactnavigation.org/docs/params/
However, all of those examples only work if you are calling navigation.navigate manually and passing the params. For example:
<Button
title="Done"
onPress={() => {
// Pass and merge params back to home screen
navigation.navigate({
name: 'Home',
params: { post: postText },
merge: true,
});
}}
/>
I have a screen with a back button, where I can call navigation.navigate and pass params on button press, like in the example above. However, the user can also swipe from the left to go back to the first screen on Android (and I'm assuming iOS as well).
So, my question:
Is there a way for me to pass the same data to the previous screen when the user swipes to go back (instead of pressing the back button)?
There might be a better way that I am unaware of. However, we can achieve this manually by preventing the default back action and handling this ourselves.
Suppose that you have a screen B and you want to swipe back to go to a screen Home and pass params to Home from B on that swipe action. Then, you can achieve this as follows.
function B({navigation}) {
React.useEffect(() => navigation.addListener('beforeRemove', (e) => {
// the navigation.navigate will fire beforeRemove which causes an infinite loop. we guard this here
if (e.data.action.type === "NAVIGATE") {
return
}
// Prevent default behavior of leaving the screen
e.preventDefault();
// navigate manually
navigation.navigate({
name: 'Home',
params: { post: postText },
merge: true,
});
}), [navigation]);
return ( ... )
}
Edit: The navigation.navigate fires the beforeRemove event again, obviously, which causes an infinite loop. We can guard this as shown above.
The context is a simple React Native app with React Navigation.
There are 3 screens.
The first simply displays a button to go to second screen using navigation.navigate("SecondScreen").
The Second contains a hook (see code below) that adds a listener to listen the mouse position. This hook adds the listener in a useEffect hook and removes the listener in the useEffect cleanup function. I just added a console.log in the listener function to see when the function is triggered.
This screen contains also a button to navigate to the Third screen, that only shows a text.
If I go from first screen to second screen: listener in hook start running. Good.
If I go back to the first screen using default react navigation 's back button in header. the listener stops. Good.
If I go again to second screen, then listener runs again. Good.
But if I now go from second screen to third screen, the listener is still running. Not Good.
How can I unmount the hook when going to third screen, and mount it again when going back to second screen?
Please read the following before answering :
I know that:
this is due to the fact that react navigation kills second screen when we go back to first screen, and then trigger the cleanup function returned by the useEffect in the hook. And that it doesn't kill second screen when we navigate to third screen, and then doesn't trigger the cleanup function.
the react navigation's hook useFocusEffect could be used to resolve this kind of problem. But it can't be used here because it will involve to replace the useEffect in the hook by the useFocusEffect. And I want my hook to be usable in every context, even if react navigation is not installed. More, I'm using here a custom hook for explanation, but it's the same problem for any hook (for example, the native useWindowDimensions).
Then does anyone know how I could manage this case to avoid to have the listener running on third screen ?
This is the code of the hook sample, that I take from https://github.com/rehooks/window-mouse-position/blob/master/index.js, but any hook could be used.
"use strict";
let { useState, useEffect } = require("react");
function useWindowMousePosition() {
let [WindowMousePosition, setWindowMousePosition] = useState({
x: null,
y: null
});
function handleMouseMove(e) {
console.log("handleMouseMove");
setWindowMousePosition({
x: e.pageX,
y: e.pageY
});
}
useEffect(() => {
window.addEventListener("mousemove", handleMouseMove);
return () => {
window.removeEventListener("mousemove", handleMouseMove);
};
}, []);
return WindowMousePosition;
}
module.exports = useWindowMousePosition;
the react navigation's hook useFocusEffect could be used to resolve this kind of problem. But it can't be used here because it will involve to replace the useEffect in the hook by the useFocusEffect. And I want my hook to be usable in every context, even if react navigation is not installed
So your hook somehow needs to know about the navigation state. If you can't use useFocusEffect, you'll need to pass the information about whether the screen is focused or not (e.g. with an enabled prop).
function useWindowMousePosition({ enabled = true } = {}) {
let [WindowMousePosition, setWindowMousePosition] = useState({
x: null,
y: null
});
useEffect(() => {
if (!enabled) {
return;
}
function handleMouseMove(e) {
console.log("handleMouseMove");
setWindowMousePosition({
x: e.pageX,
y: e.pageY
});
}
window.addEventListener("mousemove", handleMouseMove);
return () => {
window.removeEventListener("mousemove", handleMouseMove);
};
}, [enabled]);
return WindowMousePosition;
}
And then pass enabled based on screen focus:
const isFocused = useIsFocused();
const windowMousePosition = useWindowMousePosition({ enabled: isFocused });
Note that this approach will need the screen to re-render when it's blurred/focused unlike useFocusEffect.
someone can help to set my drawer menu permanent depending of the user device ?
I was thinking of using the vuetify grid system to find out if I'm in lg or md or sm, but I don't know how to do it.
This is my drawer menu code :
<v-navigation-drawer
#input="updateDrawer"
:hide-overlay="true"
:mini-variant="true"
:expand-on-hover="true"
:touchless="true"
:disable-resize-watcher="false"
style="min-width: 100px!important;"
v-model="d"
//here set this: :permanent="!isMobile"
app
clipped
>
...
</v-navigation-drawer>
thanks.
In fact, You can bind permanent directly to the breakpoint, instead of computed.
:permanent="!$vuetify.breakpoint.xsOnly"
If you wanted to still allow manual toggling of the navigation drawer (using a hamburger button), you could add another computed property for the v-model. This allows the drawer to auto show/hide based on the breakpoint or be manually toggled...
computed: {
isLarge() {
return this.$vuetify.breakpoint.name !== 'xs'
},
showDrawer() {
return this.isLarge || this.drawer
}
},
<v-navigation-drawer
v-model="showDrawer"
:permanent="isLarge"
color="primary"
absolute
dark>..
</<v-navigation-drawer>
https://codeply.com/p/Y883gzgiKe
I found this solution that works for my case. I publish in case someone wants to do the same thing as me.
vuetify can give you the user device screen size basing on grid sysmtem. To get the sreen size, do it when the component is computed.
computed: {
isComputer() {
return this.$vuetify.breakpoint.name !== 'xs';
}
}
The drawer menu code can be updated with this:
<v-navigation-drawer
...
//listen if it's a computer(in my case i want md, lg, sm as computer)
:permanent="isComputer"
app
clipped
></v-navigation-drawer>
A modal is opened, which has multiple pages. For the navigation I'm using frame navigation. There is a close button on every page clicking on which closes the modal.
What I'm doing now is passing this.$modal as a property to each page which creates a long chain of property passing and on each page I just do this.modal.close() where this.modal is the property of the component that refers to the this.$modal of the first page.
I was wondering if there was a better way, such as accessing the topmost open modal and closing it.
I'm using nativescript-vue and the builtin nativescript modals
Please note that I have multiple modals in other parts of my application. there is only this one that has navigation in it.
A small improvement could be saving the modal into the Vuex store, accessing it anytime instead of chaining props.
Detach modal component by plugin.
const modalDialog = {
install (Vue, options = {}) {
// ...
}
}
Vue.use(modalDialog)
Designate Vue prototype for plugin.
const modalDialog = {
install (Vue, options = {}) {
Vue.prototype.$modal = {
show () {
// ...
},
hide () {
// ..
}
}
}
}
Vue.use(modalDialog)
this.$modal is accessible from all components.
this.$modal.show() // or hide()
I am using a switchNavigator to display either a show view or a view where the user can add more content. I want to send back a boolean variable just as a flag, I think I have that part just right but I don't know how to make it so that my code receives it and changes view.
This is in my routes.js
let hasItems = true;
const ItemsScreens = createSwitchNavigator(
{
Items: {
screen: Items,
},
ItemsExist: {
screen: ExistingItems,
},
},
{
mode: 'card',
initialRouteName: hasItems ? 'ItemsExist' : 'Items',
navigationOptions: {
drawerIcon: getDrawerItemIcon('account-balance-wallet'),
title: `Items`,
},
},
);
inside my ExistingItems.js I have a button that does:
<Button
onPress={() => this.props.navigation.navigate('Items', {hasItems: false})}
label={'Add Items'}
/>
My idea is to call the view again but send the false value in the variable to enter the actual adding items state but I have no idea how to make it actually receive the value. I tried doing an if like:
if(this.props.navigation.state.params.hasItems)
but that is undefined and crashes.
As suggested in react navigation Authentication flows example, create one another screen AuthLoadingScreen, which checks the condition and according to condition navigate to your another screen. However, I also know that extra screen will not good for user UI but it will work around.
Which property is seen as undefined?
Aren't you also controlling the wrong variable? the param you are passing is hasBanks but you are controlling hasItems