Issue 1:
I'm submitting a ... (check one with "x")

[x] feature request

Current behavior

I have to store subscriptions and unsubscribe on destroy:

@Component({ /* ... */ })
export class MyComponent {
public data;
private dataSubscription: Subscription;

constructor(private http: Http, private route: ActivatedRoute) {
}

ngOnInit() {
this.dataSubscription = this.route.params
.switchMap(() => this.http.get('/api'))
.subscribe(response => this.data = response.json());
}

ngOnDestroy() {
if (this.dataSubscription) {
this.dataSubscription.unsubscribe();
}
}
}

Expected/desired behavior

I would rather do observable.takeUntil(componentDestroyEmitter).... like:

@Component({ /* ... */ })
export class MyComponent {
public data;

constructor(private ngDestroyEmitter: NgDestroyEmitter, private http: Http, private route: ActivatedRoute) {
}

ngOnInit() {
this.route.params
.takeUntil(this.ngDestroyEmitter)
.switchMap(() => this.http.get('/api'))
.subscribe(response => this.data = response.json());
}
}

To avoid additional Subscription state and manual cleanup code.

Angular version: 2.0.0-rc.4

Browser: [all]

Language: [all]

Issue 2:
I'm submitting a ...

[ ] bug report
[x] feature request
[ ] support request

Current behavior

Currently, component lifecycle hooks are handled by implementing one or more interfaces provided by angular. I find myself often wanting to subscribe to an observable until the component/directive is destroyed. To do this, I have to have a member for each subscription for later disposal in the ngOnDestroy() method.

Example:

/* imports */

@Directive({
selector: '[onDrag]'
})
export class OnDrag implements OnInit, OnDestroy {
dragStart: Subscription;
dragEnter: Subscription;
dragOver: Subscription;
dragLeave: Subscription;

constructor(private element: ElementRef) { }

ngOnInit(): any {
let elem = this.element.nativeElement;

this.dragStart = Observable.fromEvent(elem, 'dragstart')
.do(() =>{ /*do stuff*/ })
.subscribe();

this.dragEnter = Observable.fromEvent(elem, 'dragenter')
.do(() =>{ /*do stuff*/ })
.subscribe();

this.dragOver = Observable.fromEvent(elem, 'dragover')
.do(() =>{ /*do stuff*/ })
.subscribe();

this.dragLeave = Observable.fromEvent(elem, 'dragleave')
.do(() =>{ /*do stuff*/ })
.subscribe();

// etc...
}

ngOnDestroy(): any {
this.dragStart.unsubscribe();
this.dragEnter.unsubscribe();
this.dragOver.unsubscribe();
this.dragLeave.unsubscribe();
}
}

Desired behavior

My suggestion is to provide components with an observable for each lifecycle event (via injection perhaps?). The above example could turn into something like:

/* imports */

@Directive({
selector: '[onDrag]'
})
export class OnDrag implements OnInit {
constructor(
private element: ElementRef,
private lifecycle: ThingWithLifecycleObservablesForThisComponent
) { }

ngOnInit(): any {
let elem = this.element.nativeElement;

Observable.fromEvent(elem, 'dragstart')
.do(() =>{ /*do stuff*/ })
.takeUntil(this.lifecycle.ngDestroy$)
.subscribe();

Observable.fromEvent(elem, 'dragenter')
.do(() =>{ /*do stuff*/ })
.takeUntil(this.lifecycle.ngDestroy$)
.subscribe();

Observable.fromEvent(elem, 'dragover')
.do(() =>{ /*do stuff*/ })
.takeUntil(this.lifecycle.ngDestroy$)
.subscribe();

Observable.fromEvent(elem, 'dragleave')
.do(() =>{ /*do stuff*/ })
.takeUntil(this.lifecycle.ngDestroy$)
.subscribe();

// etc...
}
}

What is the motivation / use case for changing the behavior?

Ultimately, I would like a way to not have to hold onto subscriptions as class members (perhaps there is a better way to achieve this than my suggestion). It could be convenient/concise to be able to handle lifecycle events as observables within components. As a workaround currently I have been extending my components with a base class that provides (some) of these events:

import { OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs/Subject';

export class ComponentLifeCycle implements OnInit, OnDestroy {
protected ngInit$: Subject<any> = new Subject<any>();
protected ngDestroy$: Subject<any> = new Subject<any>();

ngOnInit(): any {
this.ngInit$.next({});
}

ngOnDestroy(): any {
this.ngDestroy$.next({});
}
}

Please tell us about your environment:

Angular version: 2.0.0-rc.4

Browser: all

Language: all