I have one suggestion for you guys for using Observable
for Output
in Angular. I thought you might not know about that because it wasn’t get mentioned in the documentation. Take a look at the below code and and I will explain on the later part.
@Output() someEvent = new EventEmitter();
this.someSource$.pipe(
/* additional logic transformation */
).subscribe(data => this.someEvent.emit(data));
@Output() someEvent = this.someSource$.pipe(
/* additional logic transformation */
);
As you can see, the before and after code was pretty much identical. Its use-case is applicable when you want to use Output
to emit something for the Parent Component when someSource$
emit data. But why it works?
To go a bit deeper, you need to understand two things:
(eventName)
EventEmitter
Event binding is what you see when using a parenthesis and a event name on the template part, e.g. <child (someEvent)="invokeSomeEvent()"></child>
.
Basically, Angular Compiler will parse all the template to Abstract Syntax Tree (AST) so that I can have all information regarding the current template. For the event binding, Angular knows which event get binding on the template itself.
If you look at source code of output_interpreter, there is one path to check if the current event is SubscribeObservable
, it will automatically subscribe to that Observable
.
What does this mean? If event is either Observable/Subject
, Event Binding syntax ()
will automatically do the subscribe.
EventEmitter
is a subclass of Subject
under the hood. And as you might have already known, Subject
acts as both Observable
and Observer
. With the code on the above output_interpreter, if you change an EvenEmitter
with a Subject/Observable, it will be automatically subscribed.
When you understand the two concepts Event Binding syntax
and EventEmitter
, I hope you get why the above code works.
Angular team also use this approach for one of their search-box component example. Thanks chandlerfang for the input!
View the source code
@Component({
selector: 'aio-search-box',
template: `
<input
#searchBox
type="search"
aria-label="search"
placeholder="Search"
(keyup)="doSearch()"
/>
`,
})
export class SearchBoxComponent implements AfterViewInit {
private searchDebounce = 300
private searchSubject = new Subject<string>()
@Output() onSearch = this.searchSubject.pipe(
distinctUntilChanged(),
debounceTime(this.searchDebounce)
)
doSearch() {
this.searchSubject.next(this.query)
}
}
Have fun!
Thanks Chau Tran for the original Vietnamese version.