-
-
Notifications
You must be signed in to change notification settings - Fork 241
HMR - Services bootstrapped multiple times, but never torn down #1605
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
Comments
@NickIliev Can you please specify the solution to this? I am having a similar issue: My app gets bootstrapped according to the number of times Hot Module Replacement has run. I see this bc I have an (i) These events fire the number of times that I have saved changes in the app, so they fire more the more hot module replacements I run. This did not happen until I tried to do hot module replacement by adding "useLegacyWorkflow": false to nsconfig.json. to get HMR to run, I also stopped calling Since then, this problem of multiplying bootstraps began. Using NS 5.3.1, Angular. Is there some kind of ngOnDestroy I need to call? What should happen in the destroy action? |
Actually, this issue can be closed.
@NL33 you have to call When you construct your service, you're subscribing to events: Now you have 2 services (one in use and the other not being referenced anywhere) that call their respectives This is my current approach (handling back button on android):
|
Thanks very much, @edusperoni. Great info. To clarify, I am currently calling suspend and resume in my app.module.ts file (that is the main spot I put those actions--I assume that is not an issue), not a service. Unless app.module.ts is actually a service. Using NS 5.3.1, Angular, focused on iOS. So, for example, here is my code: app.module.ts:
And, as discussed, with Hot Module Replacement, the suspend and resume events get called an increasing amount depending on how many times HMR has been activated (ie, how many times I have made changes, and saved those changes). So to avoid this problem, it seems like I should call ngOnDestroy() in app.module.ts. Therefore, I assume you mean I should add to app.module.ts:
I'm not familiar with the app.android.off... syntax you are using. Does the above code represent what you would have in mind? Is this just necessary if I am using HMR? (if so, for production, I might consider removing HMR and this ngOnDestroy). Related Note: I am having a possibly related issue with HMR: I have found that HMR sometimes hangs (stopping on "Webpack build done!"), and I think this happens if there is an error in my code. In the case of code error, seems like reloading is paused--which is a problem because it doesn't tell me what/where the error is! |
@NL33 You shouldn't write stuff in your AppModule constructor! Instead, use It should look like this:
You can then use it in your components:
|
Interesting. Thanks. I'll give it a shot. On app.module: One of the reasons I am using app.module instead of a service is there are other things that I am monitoring in there. For example, Apple's universal links--I need to know when a universal link has been tapped to open the app. I'm using app.ios.delegate and applicationContinueUserActivityRestorationHandler, and I don't think that works outside of app.module. So I have to: 1) register the universal link tap event, 2) get the details of the link, and then 3) send it off to the rest of the app. I don't think I can do (1) and (2) if I am not in app.module, but maybe app.component can work. BTW, I have seen other things too that I think require code do work in app.module. For example, the nativescript-purchase plugin, for in-app purchases. You have to initialize the in app purchases, and it only seems to work if the code is in app.module. |
You don't need to call them on the app module constructor. You can call them simply like this:
This way your code runs BEFORE angular is initialized. You can then set a global variable with some data which you then read when you're constructing your service (this is how most plugins are implemented). Running in your AppModule constructor is usually not recommended. If you REALLY need to do it, it's better to create a Service that you inject in your So you could have:
And it would actually initialize |
Sorry if I was unclear. The init of purchases for nativescript-purchase that happens in app.module does not happen in the constructor. Neither does the app delegate action to read the universal link. Right now, both of them happen in app.module above NgModule, like in your code. So I assume it is fine to leave those in app.module, so long as they are above the constructor. I think what you are saying is:
If that is correct, do you know how to set a global variable in code that appears before @NgModule? for example, if this is above @NgModule in app.module:
The question is, as said above, what's the code to send 'tappedUrl' into a global variable that a service can access? |
If you can contain it into a service you should probably do so (application events usually fall into this). Otherwise, you can do the following: // tapped-url.ts
export let tappedUrl = null;
export function init() {
app.ios.delegate = UIResponder.extend({
applicationContinueUserActivityRestorationHandler: function(application, userActivity) {
if (userActivity.activityType === NSUserActivityTypeBrowsingWeb) {
tappedUrl = userActivity.webpageURL **//HOW do I send tappedUrl into a global variable that other services can access?**
}
return true;
}
})
}
// app.module.ts
import { init } from './tapped-url';
init();
// myservice.ts or even CanActivate on your main route
import {tappedUrl} from './tapped-url';
// use tappedUrl If you have events that you HAVE to subscribe at app init, you can store all events in an array ( Basically, if you're dealing with stuff outside of angular, so you have to store them somewhere until angular is ready to receive it. Edit: and it's important to always make angular components/services/etc self-contained, so make sure to clean-up angular stuff inside angular (for example, if you add a handler to something non-angular, remember to remove this reference on |
Thanks. Your code works for me. I was missing wrapping the relevant function in an export function, and then calling that function in app.module. So this should all be working now (haven't had time to fully test, but I expect it works). For future reference, do you know what's so bad about having functions in app module, such as having the onsuspend and onresume events? |
Although Modules seem to have You'd expect your modules and services to be self-contained, so it's bad design to write stuff in your AppModule and have a bunch of code that depend on it on child modules (you have made the dependency arrows go both ways, now your parent depends on children and your children depend on the parent). I wrote a simple example on stackblitz that is a good starting point for anyone trying to do the same: |
Awesome. You've helped me out a lot here. Thanks. |
@larssn commented on Fri Nov 09 2018
Environment
CLI: 5.0.0
Cross-platform modules:
✔ Component nativescript has 5.0.0 version and is up to date.
✔ Component tns-core-modules has 5.0.2 version and is up to date.
✔ Component tns-android has 5.0.0 version and is up to date.
✔ Component tns-ios has 5.0.0 version and is up to date.
Android Runtime: 5.0.0
iOS Runtime: 5.0.0
Plugin(s): None
Node.js: 10.12.0
Please, attach your package.json and webpack.config.js as these configurations are usually critical for investigating issues with webpack.
These are default out of the box configs from using
tns create
Describe the bug
When HMR tears down the Angular platform on changes, it correctly calls ngOnDestroy for all components etc, however it does not for services, meaning your app can end up in a weirdly initialized state with multiple "singletons" running side by side.
To Reproduce
tns create
- create a new angular project with say, the tabbed view.Insert a constructor in app/core/data.service.ts:
constructor() { console.log("DataService constructor"); }
tns run android --hmr
- the app starts, "DataService constructor" is written in console as expected.Change some text in
home.component.html
that will cause a refresh. "DataService constructor" gets output again.Expected behavior
ngOnDestroy
is a valid lifecycle hook for services as well, as mentioned here: https://angular.io/api/core/OnDestroyYou can imagine the trouble you can get in when you start including Rx Observable subscriptions in the constructor of a service, and even more when services start bootstrapping plugins.
So
ngOnDestroy
should also get called for services.The text was updated successfully, but these errors were encountered: