Skip to content

Commit e41a8b5

Browse files
committed
Add component tree
1 parent 1fcb60b commit e41a8b5

17 files changed

+167
-7
lines changed

README.md

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ https://stackoverflow.com/questions/35042929/is-it-necessary-to-unsubscribe-from
1616

1717

1818
## How to run the examples by yourself
19-
The project was set up using the latest Angular CLI. Therefore you can just clone the repository and run
19+
The project was set up using the latest [Angular CLI](https://github.com/angular/angular-cli) (version 8.3.6). Therefore you can just clone the repository and run
2020
``
2121
npm start
2222
``
@@ -233,21 +233,33 @@ this.router.events.subscribe((event) => {
233233
```
234234
To investigate how this observable behaves, we navigated to the component and then navigated to other components afterwards.
235235

236-
## Outcomes
237-
### Side effects
236+
### Outcomes
237+
#### Side effects
238238
The observables callback is still executed even when the component is destroyed. So there can be unwanted side effects,
239239
when using certain code in the callback.
240240

241-
### Memory leak
241+
#### Memory leak
242242
The above version creates a memory leak, since we use a reference to the component in the callback.
243243
Each time we navigate to the component a new dangling component (that cannot be garbage collected) is created.
244244
If no reference is used, the component gets garbage collected.
245245

246-
## Summary
246+
### Summary
247247
You should always unsubscribe when using the events observables from the ``Router`` in components
248248
(there is one exception tough, namely if you use it in the root component) as the subscription is still alive, even if
249249
the component was destroyed by Angular.
250250

251+
## Memory leaks in component trees
252+
We wondered if a subcomponent of a component with an observable that does not complete could maybe
253+
cause a memory leak on the whole component tree upwards.
254+
255+
To this end we created ``ComponentTreeComponent``, which has an infinite time observable in the most inner
256+
component.
257+
258+
### Outcomes
259+
As expected, only the component with the timer does not get garbage collected.
260+
This is good news, as this means the third party components (as used as in this example)
261+
cannot affect memory leaks on your components.
262+
251263
## Recommended ways to unsubscribe
252264
The obvious way of unsubscribing is how it is done in our examples: Assign the subscription to a class
253265
property and unsubscribe in the ``ngOnDestroy()`` method.

angular-app/src/app/app-routing.module.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {RouterModule, Routes} from '@angular/router';
33
import {EmptyComponent} from './empty/empty.component';
44
import {RxjsTimerComponent} from './rxjs-timer/rxjs-timer.component';
55
import {
6+
PATH_COMPONENT_TREE,
67
PATH_EMPTY,
78
PATH_HTTP_CLIENT,
89
PATH_ROUTER_EVENTS,
@@ -14,6 +15,7 @@ import {RxjsTimerCompleteComponent} from './rxjs-timer-complete/rxjs-timer-compl
1415
import {HttpclientComponent} from './httpclient/httpclient.component';
1516
import {RouterParamMapComponent} from './router-param-map/router-param-map.component';
1617
import {RouterEventsComponent} from './router-events/router-events.component';
18+
import {ComponentTreeComponent} from './component-tree/component-tree.component';
1719

1820

1921
const routes: Routes = [
@@ -23,7 +25,8 @@ const routes: Routes = [
2325
{path: PATH_HTTP_CLIENT, component: HttpclientComponent},
2426
{path: PATH_ROUTER_PARAM_MAP, component: RouterParamMapComponent},
2527
{path: PATH_ROUTER_PARAM_MAP + '/:param', component: RouterParamMapComponent},
26-
{path: PATH_ROUTER_EVENTS, component: RouterEventsComponent}
28+
{path: PATH_ROUTER_EVENTS, component: RouterEventsComponent},
29+
{path: PATH_COMPONENT_TREE, component: ComponentTreeComponent}
2730
];
2831

2932
@NgModule({

angular-app/src/app/app.component.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ <h1>Example Angular app with two routes</h1>
2525
<li>
2626
<a [routerLink]="RoutingLinks.routerEvents">Angular router events</a>
2727
</li>
28+
<li>
29+
<a [routerLink]="RoutingLinks.componentTree">Component tree</a>
30+
</li>
2831
</ul>
2932
<hr>
3033
<router-outlet></router-outlet>

angular-app/src/app/app.module.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ import { TakeUntilComponent } from './unsubscription-methods/take-until/take-unt
1313
import { UntilDestroyedComponent } from './unsubscription-methods/until-destroyed/until-destroyed.component';
1414
import { RouterParamMapComponent } from './router-param-map/router-param-map.component';
1515
import { RouterEventsComponent } from './router-events/router-events.component';
16+
import { ComponentTreeComponent } from './component-tree/component-tree.component';
17+
import { SubComponentOneComponent } from './component-tree/sub-component-one/sub-component-one.component';
18+
import { SubComponentTwoComponent } from './component-tree/sub-component-one/sub-component-two/sub-component-two.component';
1619

1720
@NgModule({
1821
declarations: [
@@ -25,7 +28,10 @@ import { RouterEventsComponent } from './router-events/router-events.component';
2528
TakeUntilComponent,
2629
UntilDestroyedComponent,
2730
RouterParamMapComponent,
28-
RouterEventsComponent
31+
RouterEventsComponent,
32+
ComponentTreeComponent,
33+
SubComponentOneComponent,
34+
SubComponentTwoComponent
2935
],
3036
imports: [
3137
BrowserModule,
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<p>Component tree</p>
2+
<app-sub-component-one></app-sub-component-one>

angular-app/src/app/component-tree/component-tree.component.scss

Whitespace-only changes.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { ComponentTreeComponent } from './component-tree.component';
4+
5+
describe('ComponentTreeComponent', () => {
6+
let component: ComponentTreeComponent;
7+
let fixture: ComponentFixture<ComponentTreeComponent>;
8+
9+
beforeEach(async(() => {
10+
TestBed.configureTestingModule({
11+
declarations: [ ComponentTreeComponent ]
12+
})
13+
.compileComponents();
14+
}));
15+
16+
beforeEach(() => {
17+
fixture = TestBed.createComponent(ComponentTreeComponent);
18+
component = fixture.componentInstance;
19+
fixture.detectChanges();
20+
});
21+
22+
it('should create', () => {
23+
expect(component).toBeTruthy();
24+
});
25+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { Component, OnInit } from '@angular/core';
2+
3+
@Component({
4+
selector: 'app-component-tree',
5+
templateUrl: './component-tree.component.html',
6+
styleUrls: ['./component-tree.component.scss']
7+
})
8+
export class ComponentTreeComponent implements OnInit {
9+
10+
constructor() { }
11+
12+
ngOnInit() {
13+
}
14+
15+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<p>Component 1</p>
2+
<app-sub-component-two></app-sub-component-two>

angular-app/src/app/component-tree/sub-component-one/sub-component-one.component.scss

Whitespace-only changes.

0 commit comments

Comments
 (0)