Manually remove (and destroy) a component from keep-alive - javascript

I can't find how to get access to a component instance loaded from Vue Route, and persisted by means of <keep-alive> and <component>, so that I can programmatically unmount it.
I'm implementing a dynamic tab system so that each tab renders a URL, which in turn displays its declared component. I don't want to unmount a component everytime I load a new route whenever I create a new tab, or select another tab. I achieved it by combining with keep-alive/component in a custom component called MultiTabs:
<multi-tabs>
<router-view v-slot="{ Component }">
<keep-alive>
<component :is="Component" />
</keep-alive>
</router-view>
</multi-tabs>
So far so good. Let's say I've got these routes:
//...
{
path: '/foo',
name: 'Foo',
component: () => import("./components/pages/Foo.vue"),
},
{
path: '/bar',
name: 'Bar',
component: () => import("./components/pages/Bar.vue"),
},
//...
router.beforeEach((to, from, next) => {
store.dispatch('addTab', to); // --> Vuex would then add this route to a list of tabs (routes), which I will retrieve from the MultiTabs component.
next();
});
From my MultiTabs component, I get all the tabs (queried routes), and display them, as the tabs navigation, and the component that should be rendered:
MultiTabs.vue:
<template>
<!-- ... -->
<!-- MultiTab has a close button which emits the tabClosed event -->
<multi-tab
v-for="route of storedTabs"
:key="route.fullPath"
:route="route"
#selectedRoute="selectRoute"
#tabClosed="closeTab"
>
</multi-tab>
<!-- ... -->
<!-- This is where the component for the selected route is rendered -->
<div class="tab-pane">
<slot/>
</div>
</template>
So, when the user clicks on the close button in a tab, I want to free the corresponding component from memory. Note that this won't happen navigating through tabs because I used keep-alive and want this behaviour, so that the component is not mounted again and it performs the async calls to the server again.

Unfortunately for you such functionality does not exist yet. There are few issues in Vue repo including this one where you can find a hacky sample how to do that but it uses some internal details so using it might be risky as those details can change anytime...
Here is a ongoing discussion/RFC so if you need this, feel free to engage in the discussion or vote
Until the mentioned functionality is implemented, your best option is probably to refactor your components and do what you need to do not only in created/mounted hooks but also in activated hook which is called when kept-alive component is activated

Related

Vue Router - logging out, main app template shows for few seconds along with login component

I have an issue where my main UI is still displayed for a few seconds after clicking a logout button.
As a result the login form component displays momentarily inside the main UI template before going on to display on a blank page.
I have an App.vue file with this set up.
<template>
<v-app id="app">
<template v-if="!signedIn">
<router-view></router-view>
</template>
<template v-if="signedIn">
// main app UI here ...still shows for a couple of seconds with the login form inside it
</template>
</template>
The signedIn value determines if the main app ui is shown or not. It works, but as I say there is a some time where both the main UI and the login form appear in the interface.
I have the following route middleware set up where the userModule.checkUserSignedIn simply gets the current value of signedIn.
function requireAuth(to: TODO, from: TODO, next: TODO) {
if (!userModule.checkUserSignedIn()) {
console.log('User not signed in..')
next({
path: "/login",
query: { redirect: to.fullPath }
});
} else {
console.log('User is signed in..')
next();
}
}
})
The login route is simple.
{ path: "/login", component: Login, name: "Login" }
I am not sure what is causing this to happen and have run into a bit of a dead end. If anyone can help me figure out how to fix this I'd much appreciate it. Thanks in advance!
i am not really sure about the solution by here are some ideas might help you
make sure the default value of signedIn = false
use v-if & v-else instead of v-if & v-if
wish you good luck

reactjs - show dialog on all pages using a button component, wherever it is included

In my react app, I have a header with a show dialog button.
This header component is included in all pages as necessary.
My current logic to show the dialog is as follows:
When user clicks the button, I dispatch an action to redux store { type:SHOW_DIALOG }, and let the parent component handle the state change and draw the dialog. I am using material-UI swipeable drawer component.
Handle Click event in an iconbutton in header component...
const handleClick = () => {
/*
* temporary bypass surgery to avoid going through login dialog
* dispatch({type:'SHOW_CONTACT_INFO'})
* return;
*/
if (!isLoggedIn) dispatch({type: 'SHOW_LOGIN_DLG'})
else router.push('/my-account')
}
In the parent page, where the header is contained...
// ...other code
<Container>
{/* Whether or not to show the login dialog . */}
{loginProcessState>=1 && loginProcessState<=7
?<LoginDialog type={loginProcessState===1?"login":"otp"} />
: null}
{/* if contact edit dialog is set to show, show it. */}
{contactEditDisplay? <ProfileEditDialog />:null}
</Container>
);
But, since I have many pages (around 10 pages and server side rendering with NextJS), I will have to repeat the state management in all those pages where the dialog must be shown.
Excuse my lack of knowledge here. Is it possible to avoid this dialog state check in the parent component / page. If so, how can i do it?
Suggestion:
Move the LoginDialog component up and render it directly within the root <App/> component.
And then use React.useContext to call that dispatch in various components to trigger loginProcessState.
The above should help you render the Login acros all components.

On some pages don't show Menu Component but it is visible for a while

I would like to don' show Menu component on login and register pages. I created two layouts. One with Menu and second without. next I added meta to link in vue router
{
path: "/login",
name: "Login",
meta: {
layout: "NoMenu",
},
component: () =>
import(/* webpackChunkName: "login" */ "../views/Login.vue"),
},
And Here is function in App
<template>
<Component :is="layout"></Component>
</template>
[...]
computed: {
layout() {
return (
this.$route.meta.layout || "Default") + "-layout";
},
},
The problem is when I go to the login page Menu is visible for a while (less than one sec). I think this might happen, bcs vue-router loads synch and component is mounted before function gets this.$route. Any ideas to fix this showing menu for a while?
What you looking for is maybe v-if. From the documentation:
The directive v-if is used to conditionally render a block. The block will only be rendered if the directive’s expression returns a truthy value.
If you use your component like this <Component v-if="showMenu"></Component> and set showMenu as a boolean data property initializing as false, you can determine when it should show.
This could be a state, when the user is logged in. As I understand you, you only want to hide the menu, when a user isn´t logged in. So this could be an approach for you.
Further informations: Conditional Rendering

How to update route without re-rendering page (opening / closing a modal that has its own route)?

In my routes I have set-up a slug for a particular route like so:
Main route: /members
Sub route: /members/:slug
Now when I go to www.website.com/members/some-slug
I will try to detect whether there's a slug or not with:
if (this.props.match.params.slug) {
// If have slug, open a modal window with data that corresponds to that slug
this.showModal(this.props.match.params.slug);
}
What's happening is that showModal will change the state of a Modal component to true (thus reveal it) and also trigger an API call to get the details pertaining to the slug parameter that's passed (e.g. the slug sarah-smith is used for a GET request as a param to get Sarah Smith's details).
So all of these are fine so far. However, the issue is the re-rendering of the page when I either:
Open the modal
Close the modal
We have transitions for each element on the page so if you visit a route, the elements will have a subtle fade-in transition.
When a modal is clicked what I do is (member.name being the slug):
<Link
to={`/member/${member.name}`}
</Link>
Which causes a re-routing hence you can see those small transitions in the background while the modal is fading into view (as if I am opening /member/slug for the first time).
Same with the modal close button, if I click on it, what I do is invoke:
closeModal() {
this.props.history.push("/member");
}
Which re-renders the entire page /member page and not just close the modal. I can't just use this.setState({ showModal: false }); since that wouldn't change the route back to /member.
Is there a way to resolve this? Or another way of doing it so that the page doesn't re-render while changing the URL?
You can wrap your modal in route like this.
Modal
Then this.props.history.push("/path");
I think React-Router docs already have such examples. You can check following
https://reacttraining.com/react-router/web/example/modal-gallery.
There is npm module available as well https://github.com/davidmfoley/react-router-modal

How to create multiple Vue components inside of Bootstrap tabs?

I am using the togglable tabs component in Bootstrap. I would like a table in each tab. Each one is displaying a table of email logs (Click Event, Open Event, etc). I would also like each table to be loaded dynamically with Vue-resource only once the user clicks on that tab, and for the resource/component to only be loaded once (once we have the data from AJAX, don't refresh it).
How can I set this up? I currently have an email-table component and an email-table-template template that renders the table, but I'm not sure how to set those up to render themselves when the user clicks the tab, and to only call the AJAX once.
An illustration of the task
Here is my current code for detecting the tab switch and newing up a Vue component:
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
var email_event = $(e.target).data('email-event');
switch(email_event) {
case 'click':
createClick();
break;
// rest of the cases
}
function createClick() {
var click_events = Vue.resource('/api/email_logs/click_events');
click_events.get().then((response) => {
new Vue({
el: '#table-click',
data: {
searchQuery: '',
gridColumns: ['campaign_id', 'target_link_name', 'target_link_url', 'created_at'],
gridData: response.body
}
})
});
Any insight is appreciated. Thanks very much!
If you want to call a method only once you can use listen and emit events.
vm.$once and vm.$emit should do the trick.
Official documentation
https://v2.vuejs.org/v2/api/#vm-once
Here is a quick example
https://jsfiddle.net/leocoder/s1nfsao7/4/
From official documentation
If you want to keep the switched-out components in memory so that you can preserve their state or avoid re-rendering, you can wrap a dynamic component in a <keep-alive> element
Sample:
<keep-alive> <component :is="currentView"> <!-- inactive components will be cached! --> </component> </keep-alive>
In the above example "currentView" is to be set to a component name you want to load/display
Docs: https://v2.vuejs.org/v2/api/#keep-alive

Categories

Resources