New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Programmatically prevent the destruction of components when requested #5275

Closed
cangosta opened this Issue Nov 13, 2015 · 68 comments

Comments

Projects
None yet
@cangosta
Copy link

cangosta commented Nov 13, 2015

It would be nice if we could tell angular not to destroy components when we navigate out of them, even if the next component is of a different type. This could be useful, for example, when building a window management system, where users can navigate through different windows (components) without
the need for recovering the whole state.

The existing canReuse hook is not useful for this use case, since it only allows us to reuse components if the previous and the next component have the same type.

Code example:

@Component({...})
class ExampleComponent {
    canDestroy() { // or some other name
      return this.closeButtonClicked;
    }
}
@mhevery

This comment has been minimized.

Copy link
Member

mhevery commented Nov 15, 2015

Why can't you just store the state in higher level component which does not get destroyed?

@cangosta

This comment has been minimized.

Copy link

cangosta commented Nov 15, 2015

Hi Misko, thanks for you answer! I've actually done that and it works fine. Take this just as a suggestion. There may be a lot of state that must be stored for complex windows and doing that is error prone and painful. It would be nice if angular 2 had this possibility.

@hitmanmcc

This comment has been minimized.

Copy link

hitmanmcc commented Nov 16, 2015

I agree with @cangosta, I think this could be a very useful feature in certain scenarios. However, I don't know if this is something that could be easily added to the framework or if it would be a costly both in terms of development and/or computational resources. Can someone please weigh in on this? I'd be interested in seeing this be discussed in a somewhat technical fashion.

@tbragaf

This comment has been minimized.

Copy link

tbragaf commented Nov 17, 2015

I have to agree with what was said.

Picture the scenario when you need to navigate through various routes.
If you save state in a higher level component, you most likely will save the model.
What about the state of the view?

Imagine you have in one view a tab component, with the 3rd tab selected.
When getting back, you want the 3rd tab to be selected.

This is no longer be a model concern, so should I save it anyway?
What about multiple tab components? Save all of them?

Save everything ad hoc?

@mhevery

This comment has been minimized.

Copy link
Member

mhevery commented Nov 18, 2015

I think you can already do this.

  1. Hide the component through hidden property
  2. Opt out of change detection using ChangeDetectionStrategy

Closing since I don't think we will do anything more specific than the above

@mhevery mhevery closed this Nov 18, 2015

@tbragaf

This comment has been minimized.

Copy link

tbragaf commented Nov 18, 2015

@mhevery
Good ideia, but what if those components are bound to a router (which is the actual use case)?

The router is the one managing that.

@mhevery

This comment has been minimized.

Copy link
Member

mhevery commented Nov 18, 2015

@btford do you have an idea on this one? I think it would be reasonable to keep the Viewref around and disable the change detection. Need to do some more thinking.

@cangosta

This comment has been minimized.

Copy link

cangosta commented Nov 19, 2015

In order to make it explicit, what is intended here is to somehow inform angular that we don't want it to destroy a view when we navigate out of it and then, recover that same view when we navigate to its path. Afaik, @mhevery's suggestion of hiding components would work fine, if we had an easy way to explicitly instantiate a view (which is a component) and then render it.

@hitmanmcc

This comment has been minimized.

Copy link

hitmanmcc commented Nov 23, 2015

This is a very interesting problem and one that I think has far reaching implications in terms of developer burden and user experience. I'll explain what I mean with a couple of scenarios.
If we imagine using angular 2 for a mobile application, consider something like Facebook's app. Then we'll have a sort of tabbed arrangement of a main component, where we switch between content on different tabs. For this, the way angular 2 works now is fine, our data will never be discarded by change detection.

However, if we imagine a case where we have a more complex type of application, something that is meant for a commercial/enterprise environment, then we can run into some serious issues. Let's consider a scenario that is actually viable both for desktop and mobile. Imagine that we have an application that allows a user to manage the business activities of his shop. He's trying to issue some for of document, an invoice for instance and he's met by a new client that's he's going to add to his database. Adding the client will mean navigating away from the component where he was issuing the invoice, he fills out the form, saves it and then he comes back to the screen where he was registering the sale and voilà, the data he previously input is gone.

This would be an even more serious loss of productivity if he's working on a document that affects inventory/stock, when there are possible tens of thousands of rows on the document, which would all get lost. Now if we consider a scenario where the client was actually working on a mobile device, like a table, this problem gets even more compounded because the computational resources are far inferior and the connectivity is usually flakier.

So either the client risks losing work, or there need to be checks set in motion to prevent him from performing certain operations at certain times, or an overhead is imposed on development where programmers will have to come up with a means of storing and restoring state across application components. Which also has computational costs that may/(likely will) exceed some solution/mechanism that is integrated into the framework.

@jorgeunimicro

This comment has been minimized.

Copy link

jorgeunimicro commented Nov 26, 2015

+1 to this problem.

That is the problem I have found:

  • I apply some directives to form inputs
  • Those directives manipulate the dom to add some behavior (multiselect, maksed inputs...)
  • Each time I come back to the view html is cached but not directives so they run constructor and lifecycle hooks again and manipulate the dom again.

It would be nice if there can be a strategy to keep the state of the component (maybe reuse the instance of the class, not sure how difficult it is or if it is possible)

Thank you.

@jorgeunimicro

This comment has been minimized.

Copy link

jorgeunimicro commented Nov 27, 2015

My solution for the moment is to create a destroy method in each directive that cleans up the autogenerated HTML and cleans up attached events also, so directive can re-run again.

Maybe would be helpful if you can tell the router the strategy to follow: to cache or not to cache views, so developer can asume freely risks of each strategy.

@yvoronen

This comment has been minimized.

Copy link

yvoronen commented Dec 15, 2015

+1
I ran into same issue. Multi-tab application using the router, and I would like to preserve the state of each tab, while switching tabs.

I can certainly use a service, but that requires extra implementation effort for every tab involved.

This is super useful in complex multi-tab applications.

@waeljammal

This comment has been minimized.

Copy link

waeljammal commented Jan 19, 2016

+1 I am using CanReuse and OnReuse but it still calls my constructor every time my use case is a content editor and it loads lots of assets into an iframe so I would rather not reload all of that every time the user comes back to the view.

@hdeshev

This comment has been minimized.

Copy link

hdeshev commented Jan 19, 2016

Just stumbled into this issue, and wanted to provide another valid (IMO) use case.

I'm trying to make the Angular router and the NativeScript navigation framework work together. In a NativeScript mobile app you can have different pages, and you can navigate to them, displaying different parts of your UI. The mobile toolkit makes it all look nice, using animated transitions, etc.

My initial attempt at this involved the following:

  • Navigate to a "page shim" component, AsyncRoute-style.
  • Once the page shim component loads, trigger navigation to a new mobile page.
  • Load the new page content, given a component type using the DynamicComponentLoader, and move the instantiated visual tree to the new page right before it displays.
  • On navigating back, destroy the child page, unload the component, and resolve the shim component deactivation promise once the original page is fully transitioned to.

This approach works pretty well most of the time. It fails in cases where you can use a swipe gesture to preview the original page before navigating back to it. At that point in time the original page is mostly blank and looks broken because the router has deactivated the original component.

I'll be revisiting this problem in the next couple of days and look for a way to preserve the original view state when navigating to a new component. For example, I haven't yet explored the possibility of building a custom router outlet component for that specific scenario.

@Namek

This comment has been minimized.

Copy link

Namek commented Jan 31, 2016

I have a simple case, imagine routes that have tabs inside tabs:
/product/1/info
/product/1/buy
/product/2/info
/product/2/buy

I expect to see 2 instances of Product component, each having one instance of ProductBuy and ProductInfo. Everytime I switch between any of those tabs, nothing should be destroyed/recreated, just hiding/unhiding elements. And - lazy loading.

Router in current shape is somehow useless to my app which I rewrite from Angular 1. A bit of shame because Angular 2 itself is awesome!

@mnvx

This comment has been minimized.

Copy link

mnvx commented Feb 23, 2016

+1 Will be nice to store states with Angular2 capacibilities

@tbragaf

This comment has been minimized.

Copy link

tbragaf commented Feb 26, 2016

@hdeshev
That is an adhoc workaround.
Currently we have no option besides that one and that is why the issue is opened.

@alex321

This comment has been minimized.

Copy link

alex321 commented Mar 10, 2016

+1. I was hoping this feature would be in Angular 1 UI router

It would be a great feature instead of having to hide and show elements in order to achieve the same for the view.

@fabfire

This comment has been minimized.

Copy link

fabfire commented Mar 10, 2016

+1 for this feature request.

I encouter the same issue, here is my use case :
I have a menu with 2 options that show the corresponding component using router outlet.
The 1st show charts that are dynamically updated with values provided by socket.io.
The 2nd is a log view that show messages sent be socket.io.

With the current behavior, I have to store all the values (in a Service) to be able to redraw the charts each time I switch between the chart component and the log component.
And it is the same for the log view.

Could you please add a feature to store the state of a component view or not destroying it ?
Thank you

@danielrasmuson

This comment has been minimized.

Copy link
Contributor

danielrasmuson commented Mar 11, 2016

+1

2 similar comments
@chasemgray

This comment has been minimized.

Copy link

chasemgray commented Mar 11, 2016

+1

@MurhafSousli

This comment has been minimized.

Copy link

MurhafSousli commented Mar 16, 2016

+1

@vakrilov

This comment has been minimized.

Copy link

vakrilov commented Sep 2, 2016

@zoechi You are right about the plans about caching. #7757 is the issue you are probably referring to and more specifically this comment. It looks like a duplicate, but it is still open so you can follow that one as well.

@Namek I agree with you that performance is a good reason for caching. In the NativeScript scenario on the other hand - the native navigation stack already does the caching - so there is no reason not to use it.

@kylecordes

This comment has been minimized.

Copy link

kylecordes commented Sep 2, 2016

@Namek I wasn't trying to be divisive, or to pre-assume other people's needs. Rather I think Angular's design is pressing all of us toward treating the thing you are describing as application state, and managing it in one of the ways I talked about. If there is some aspect of a component data (and that data might even be DOM, particularly if that DOM is created and managed by a third-party library being integrated), the current Angular feature set pushes us in one of these directions:

  • Wait for additional component caching/reuse configurability to arrive
  • Stash the data (even DOM!) we need to preserve in a service, so as to obtain the desired performance even while letting Angular manage the instantiation and re-instantiation of the Components.
  • Don't use routing or NgIf, but rather keep the components in existence and manage their visibility, perhaps with a custom tab set widget designed to work this way.
  • Possibly other variations I haven't thought of.

We have solved this problem in real apps using the third bullet point above, but I agree with you that it would be good if Angular itself offered a more built in way to do so. The need, in some cases, to keep DOM around and hide/show versus destroy/re-create, predates Angular and is not going away anytime soon. It matters for performance as you said.

@AlGantori

This comment has been minimized.

Copy link

AlGantori commented Sep 4, 2016

This discussion is a bit over my head but it sounds very related to my recent posting [(http://stackoverflow.com/questions/39299478/how-to-keep-certain-ionic2-pages-live-throughout-a-session)]

Could one of you please confirm, it is indeed related to angular2 and not ionic2, in that NavController.push() always recreates my player page, the one I want to travel to but I do not want destroyed, Because, setting up a player like youtube, a slideshow, etc... is costly and thus problematic to keep recreating with every visit to its view. Is there any workaround or fix at this time? I am new to angular2/ionic2 and would appreciate a bit of help to keep progressing with the development of my app. Hosting my players into tabs keeps them around for the session but it is not a clean user interface given the small real estate on mobile devices.

I put the following plunker together to demonstrate the current behavior of NavController.push( Player1 ) causing Player1 page instance to be created on each navigation.
http://plnkr.co/edit/aBUxw9hbe01JItoBJfl3?p=preview

Thank you.

@ronenmiller

This comment has been minimized.

Copy link

ronenmiller commented Jan 30, 2017

I am also wandering about the routing issue. Can anyone share what to do as workaround for routing back to previous component state?

@zoechi

This comment has been minimized.

Copy link
Contributor

zoechi commented Jan 30, 2017

@ronenmiller perhaps http://stackoverflow.com/questions/33940095/angular2-routing-keeping-state-of-component-when-route-changes/36010817#36010817 is helpful for you (for example the link to sticky-routes...) about custom reuse strategy

@ronenmiller

This comment has been minimized.

Copy link

ronenmiller commented Jan 31, 2017

@zoechi Thanks a lot! This is exactly what I needed. Works like a charm!

@tettusud

This comment has been minimized.

Copy link

tettusud commented Mar 10, 2017

@zoechi, but sticky routes doesnt work in case of lazy loaded modules.

@zoechi

This comment has been minimized.

Copy link
Contributor

zoechi commented Mar 10, 2017

@tettusud sorry, don't know about that. I haven't played with lazy loaded modules or custom reuse strategy yet.

@tettusud

This comment has been minimized.

Copy link

tettusud commented Mar 11, 2017

Thank you @zoechi ,do you have any working example to implement tabs in angular 2 ,I need to open routes in New tabs

@zoechi

This comment has been minimized.

Copy link
Contributor

zoechi commented Mar 11, 2017

I don't know what exactly you mean with "open routes in new tabs". If you mean browser tabs, then this won't work. Please use other channels for support questions (Gitter, StackOverflow, Google Groups, ...)

@jorgeunimicro

This comment has been minimized.

Copy link

jorgeunimicro commented Mar 16, 2017

Last think I have read about reusing components is this post

https://www.softwarearchitekt.at/post/2016/12/02/sticky-routes-in-angular-2-3-with-routereusestrategy.aspx about RouteReuseStrategy

I think that is the behaviour most of us are looking for.

Unfortunately, this example throw some errors when you navigate from one route to other. I have tried to understand the behaviour but I found the CustomStrategy storing just the top routes of the application and also giving me bad handlers. Maybe I am not understanding how it works.

I wonder if someone from the angular team could give us a good example about how to reuse those components that we have already visited.

CC @vsavkin

@tettusud

This comment has been minimized.

Copy link

tettusud commented Mar 25, 2017

@jorgeunimicro it still fails for lazyloaded module, After hours of struggle still not able to find a way to achieve tab support,
if someone finds a way to achieve a way to provide tab support please let me know.

@tettusud

This comment has been minimized.

Copy link

tettusud commented Mar 25, 2017

@zoechi My requirement is simple, we have just two Views , on same page( you can read two sub modules CustomerModule,ProductsModule with its own routings) for eg: customer details view and available product details view.
As a user I need to search for customer and load his details and products on one one view,fill customer product details/update his details while referring to other view product.
So basically I may need to switch back and forth between this two views ( like tabs, if you see bootstrap tabs,where you click on tab heading it will hide/show the tab you care clicking and clicking out ,here am not at all talking about browser tabs,but a kind of view within same page called TABS).
Two routerLinks (you can image it as tab header) one for Customer Details when clicked that loads CustomerModule, and other one is Product details which loads Product Module.
so when i switch back ,already entered data are lost, yes I tried routeReuseStrategy ,but yet it has its own issue, when lazyloading a module ,there is no clear way to handle storing similar routes like /customer/details/1 and /customer/details/2 which maps to same pattern.
I hope I gave you detail description of what is required, please let me know if you need some more information

@zoechi

This comment has been minimized.

Copy link
Contributor

zoechi commented Mar 26, 2017

@tettusud I think you should ask at one of the channels mentioned in CONTRIBUTING - Got a Question or Problem?
GitHub is for bug reports and feature requests only

@Namek

This comment has been minimized.

Copy link

Namek commented Apr 4, 2017

@tettusud if you'd be interested I could describe how I achieved dynamic tabs but without routing.

@tettusud

This comment has been minimized.

Copy link

tettusud commented Apr 4, 2017

@vinaysoni

This comment has been minimized.

Copy link

vinaysoni commented Apr 4, 2017

Can't believe that Angular team is not willing to fix this issue. This is the behavior that 99% of the apps adopt. It is funny to have a router that creates and destroys components every time renavigation occurs. Solutions have been provided by users:
https://gist.github.com/danielrasmuson/89cde9ce22d89167cec6d7f9a9240558
But the Angular team doesn't even recognize the problem.

@zoechi

This comment has been minimized.

Copy link
Contributor

zoechi commented Apr 5, 2017

@vinaysoni I think you're looking for #13124 not too well documented yet AFAIK. https://www.softwarearchitekt.at/post/2016/12/02/sticky-routes-in-angular-2-3-with-routereusestrategy.aspx might help a bit.

@Namek

This comment has been minimized.

Copy link

Namek commented Apr 5, 2017

@zoechi well, this article you mentioned is a liar. ReuseStrategy is not Sticky Route. It's a different thing behaving similarly in a small scope.

@zoechi

This comment has been minimized.

Copy link
Contributor

zoechi commented Apr 5, 2017

@Namek just because it's not exactly what you're looking for, he's not a liar.
This is the open source world. Nobody is liable to deliver something just because you think it would be handy.

@jorgeunimicro

This comment has been minimized.

Copy link

jorgeunimicro commented Apr 5, 2017

@Namek based on this article with a few modifications we have our sticky state app working. It is a little bit adhoc solution but it fixes our issues. So now we can start creating an invoice, visit the customer profile to check some values and come back to the invoice and continue the job at the same place we were.

@Namek

This comment has been minimized.

Copy link

Namek commented Apr 5, 2017

@jorgeunimicro I foresee you're using only a small subset of the desired feature. Components are reused by filling other data to previously instantiated component of same type. So, having multiple component instances of one component type you will lose state of inputs when switching because in real you'll have only one instance per type. To read another version of my explanation see #6634 (comment)

@tettusud currently I'm working on a blogpost about it. However, some people seem to upvote this http://stackoverflow.com/questions/34925782/angular2-routing-persisting-route-tabs-and-child-routes

@jorgeunimicro

This comment has been minimized.

Copy link

jorgeunimicro commented Apr 6, 2017

As far I can test I can go from customer/3 to customer/4 without problem I just store each handler in different keys of the array using the fullpath (with params). There are other problems like refresh dependant views or manage events (which still work if the instance is alive), but we have designed a pub/sub service to manage those features.

@vinaysoni

This comment has been minimized.

Copy link

vinaysoni commented Apr 17, 2017

@zoechi Thanks for the link. It does try to solve the sticky components problem. Definitely a step in the right direction. It works fine when navigating to top level paths. As soon as a path has child paths it has issues:

https://stackoverflow.com/questions/41584664/error-cannot-reattach-activatedroutesnapshot-created-from-a-different-route?answertab=votes#tab-top

#13869

@zh99998

This comment has been minimized.

Copy link

zh99998 commented Apr 20, 2017

I'm in same trouble.
my scene is just like tabs, some of them contains <iframe> tag.
when routing to another component, the iframe is removed from DOM tree and when back, it have to reload with lost states.

RouteReuseStrategy not works here.

@Namek

This comment has been minimized.

Copy link

Namek commented May 10, 2017

Well, I wrote few words about having dynamic tabs but I have ignored the routing:
https://www.namekdev.net/2017/05/angular-2-my-solution-for-dynamic-tabs/

@phil-lgr

This comment has been minimized.

Copy link

phil-lgr commented Oct 22, 2017

Any link/info of the state of "sticky state" with the final router? I have that feature with UI-Router, but would prefer to keep the default angular-router.

@erbsenkoenig

This comment has been minimized.

Copy link

erbsenkoenig commented Oct 23, 2017

@Vespira

This comment has been minimized.

Copy link

Vespira commented Jan 5, 2018

Leaving my stone here, but I agree with @kylecordes :
Immutability high level state is the best way to go here. either with :

  • a high level parent component with the OnPush Change Detection Strategy, so that the component notify changes only when new references is passed to it. You could store in a medium proper way state you need to save.

  • Or with a Redux-like state management system, with pure functions updating a state that can be observed across the application. Angular have a nice implementation of redux names @angular-redux.

It's my point of view and there's other ways to do this. I think the components destroy lifecycle is good as it is.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment