
Security News
CVE Volume Surges Past 48,000 in 2025 as WordPress Plugin Ecosystem Drives Growth
CVE disclosures hit a record 48,185 in 2025, driven largely by vulnerabilities in third-party WordPress plugins.
@typebytes/ngx-template-streams
Advanced tools
[](https://github.com/prettier/prettier) [](https://www.npmjs.com/package/@typebytes/ngx-template-
Take your Angular templates to the next level by embracing reactivity. This is a small and lightweight library that provides a simple DSL enabling event streams in templates. In other words, this library will supercharge your templates with Observables. Use a declerative syntax to create Observables from different event sources, such as native DOM events, or component outputs.
⚠️ Disclaimer ⚠️
This library is experimental and its goal is to explore how to create event streams from templates in Angular. To do this, we hook into the build process and apply HTML and TypeScript transformations, as well as patch some other internal APIs to make this work with AOT.
We like reactive programming and with this experiment we hope to push this forward within the Angular community and to help drive the adoption of a similar syntax or feature set that is integrated into Angular.
Can I use this now? Definitely! If at some point our implementation breaks, or Angular releases its own syntax, we will provide a schematic that will help you seamlessly migrate your code to keep the impact on your projects as small as possible.
ViewEngine and Ivy *ViewChild and ViewChildren (see API)$event)AsyncPipeNotes
[1] If you want to use ngx-template-streams with Ivy you have to use the latest version of ngx-template-streams.
Big thanks to Filipe Silva, Craig Spence, Alexey Zuev and Manfred Steyer for his amazing ngx-build-plus library!
The most straightforward way to get started with this library is to use its ng add schematic.
Simply run:
ng add @typebytes/ngx-template-streams
This will do all the heavy (actually not so heavy) lifting for your and add the library to your project.
Optionally you can also specifiy a project with --project <project-name>.
The schematic will:
package.jsondevDependencyserve, build, and test architects of your app (these will use a custom builder to allow for custom webpack configurations)Once all that is done, we can take advantage of this library and define some event streams in our templates 🎉.
If you want to use a more component- and code-centric way of listening for events on HTML elements or components, check out the @ObservableChild and @ObservableChildren decorator.
The syntax is simple. Here's the full specification:
(*<event-name>)="<template-stream-name>[; $event = <payload>]"
* marks the event binding as a template stream binding[] denotes optional parts of the synax<placeholder> represent placeholders you can fill inMore specifically there are 3 core building blocks:
The payload is optional and can litereally be anything as long as it follows the syntax above. So optional doesn't mean you can go wild and define the payload in whatever form you like. More on this here.
Now, let's check out how we can use this in our app 👇
Once you have installed the library, you can start using it. And that's very easy!
In order to create a template stream, you can use a slightly modified version of a regular event binding in Angular. Here's an example of a simple button with a click event:
<button (*click)="clicks$">Click Me (Stream)</button>
Instead of using a regular event binding, we are using a custom syntax that will be transformed into markup that Angular understands.
Important is that we indicate template streams by prefixing the event with an asterisk (*). For the expression of the template stream we use the name of the Observable that will emit the click event.
Note: The $ sign is only a convention and is used to denote the property as an Observable.
Next, we have to declare this property clicks$ on the component class. For that we can use a decorator provided by ngx-template-streams called @ObservableEvent():
import { Component, OnInit } from '@angular/core';
import { ObservableEvent } from '@typebytes/ngx-template-streams';
import { Observable } from 'rxjs';
@Component({...})
export class AppComponent implements OnInit {
@ObservableEvent()
clicks$: Observable<any>;
ngOnInit() {
// we can either manually subscribe or use the async pipe
this.clicks$.subscribe(console.log);
}
}
Notice that we have declared the property using the @ObservableEvent decorator and subscribe to the Observable in our ngOnInit lifecycle hook. Alternatively we can also use this property in our template again and use the AsyncPipe.
That's it! That's all it takes to create a very simple template stream!
The general syntax for creating a simple template stream inside the template is:
(*<event-name>)="<template-stream-name>; [payload]?"
The payload part is optional. For more information check out Overwriting the event payload.
By default, the event payload will be $event. Overwriting the payload is pretty straightforward and we only need to slightly extend our example from above.
So let's say we want the payload to be the string test. Then we can define the payload as follows:
<button (*click)="clicks$; $event = 'test'">Click Me (Stream)</button>
Here we slightly extend the expression with an assignment of $event. We can literally assign anything to $event, from primitive values, to objects, properties from the component, and even function calls.
The general syntax for overwriting the payload is:
$event = <value>
We have decided not to add too much magic to this library and focus a bit more on clarity over brevity, type safety and explicitness. This means that in order to add operators to the template stream, we have to declare another variable and use the template stream property (here clicks$) as the source.
For example:
import { Component, OnInit } from '@angular/core';
import { ObservableEvent } from '@typebytes/ngx-template-streams';
import { Observable } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
@Component({...})
export class AppComponent implements OnInit {
@ObservableEvent()
clicks$: Observable<any>;
// here we define a new property based on the template stream
// in order to add operators
debouncedClicks$ = this.clicks$.pipe(
debounceTime(400)
);
ngOnInit() {
this.debouncedClicks$.subscribe(console.log);
}
}
Besides the template syntax, ngx-template-streams comes with one main building block, a @ObservableEvent() decorator.
@ObservableEvent(subjectOrSubjectFactory)Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| subjectOrSubjectFactory | Subject or Subject Factory | Subject | Instance of a Subject or factory function to create the underlying source sequence for the template stream. |
For example, if we don't pass any parameters it will create a plain Subject by default.
@Component({...})
export class AppComponent {
@ObservableEvent()
clicks$: Observable<any>;
}
We can also pass in an instance of a Subject. This can be any type of Subject:
@Component({...})
export class AppComponent {
@ObservableEvent(new BehaviorSubject('INIT'))
clicks$: Observable<any>;
}
Or, we could also pass in a factory function that creates a Subject:
@Component({...})
export class AppComponent {
@ObservableEvent(() => {
return new Subject();
})
clicks$: Observable<any>;
}
@ObservableChild(selector, event, options)The ObservableChild is a reactive alternative to ViewChild.
Parameters
| Parameter | Type | Default | Optional | Description |
|---|---|---|---|---|
| selector | name or component type | / | ||
| event | event name or output | / | ||
| options | QueryOptions & AddEventListenerOptions | / | x |
QueryOptions (same options as with ViewChild)
{
static?: boolean = false;
read?: any;
}
AddEventListenerOptions
{
once?: boolean;
passive?: boolean;
}
Example:
import { Component } from '@angular/core';
import { ObservableChild } from '@typebytes/ngx-template-streams';
@Component({
...,
template: `
<button #btn>Click Me</button>
`
})
export class AppComponent {
@ObservableChild('btn', 'click', { static: true, passive: true })
clicks$: Observable<any>;
/**
* We can only start to subscribe in 'ngOnInit' if the query is 'static'.
* Otherwise we have to use 'ngAfterViewInit' or from the template
* using the AsyncPipe.
*/
ngOnInit() {
this.clicks$.subscribe(console.log);
}
}
@ObservableChildren(selector, event, options)The ObservableChildren is a reactive alternative to ViewChildren.
Parameters
| Parameter | Type | Default | Optional | Description |
|---|---|---|---|---|
| selector | name or component type | / | ||
| event | event name or output | / | ||
| options | AddEventListenerOptions | / | x |
Example:
import { Component } from '@angular/core';
import { ObservableChildren } from '@typebytes/ngx-template-streams';
@Component({
...,
template: `
<test-component></test-component>
<test-component></test-component>
<test-component></test-component>
`
})
export class AppComponent {
@ObservableChildren(TestComponent, 'myOutput')
aggregatedOutputs$: Observable<any>;
/**
* We can only start to subscribe in 'ngAfterViewInit' or from the
* template using the AsyncPipe.
*/
ngAfterViewInit() {
this.aggregatedOutputs$.subscribe(console.log);
}
}
If you want to manually install this libray you first have to install ngx-build-plus as a devDependency which allows us to extend the Angular CLI's default build behavior without ejecting:
ng add ngx-build-plus
Note: If you don't want to use the install schematic and need to know how you can manually install ngx-build-plus, I would like redirect you to the official GitHub repo.
Next, we can install ngx-template-streams and save it as a dependency of our project:
npm install @typebytes/ngx-template-streams -S
Now, we can update the angular.json and add some extra configuration options to the build, serve and test architect.
For each of those architect targets add the following additional options:
[...]
"architect": {
"build": {
[...],
"options": {
[...],
"extraWebpackConfig": "node_modules/@typebytes/ngx-template-streams/webpack/webpack.config.js",
"plugin": "~node_modules/@typebytes/ngx-template-streams/internal/plugin.js"
}
}
}
[...]
That's it! You are now ready to use template streams! 🎉
Because Observables rock 🤘. Everything is a stream and being able to also embrace reactivity in our templates improves the developer experience so that we don't have to constantly switch between different programming paradigms (imperative vs. functional reactive programming).
Also, this feature has been requested by the community for a long time and there is an open issue on GitHub since 2016.
With all the advances of different parts of the ecosystem including the Angular CLI, we wanted to take a stab and add this feature to the communities' toolbelt, allowing for more consistency in terms of programming style.
If you want to file a bug, contribute some code, or improve our documentation, read up on our contributing guidelines and code of conduct, and check out open issues as well as open pull requests to avoid potential conflicts.
This library has been well tested and works great in most use cases. However, there are a few things that we are aware of that we want to point out here. This is just to raise awareness that, in some special cases, you might notice some unexpected things.
For this library to work with AOT as well, we cannot run the type checker in a forked process. This has some performance drawbacks for large applications because TypeScript is synchronous and running the type checker in the main thread will slow down the compilation. We are aware of this and will investigate possible solutions to the underlying error, that is the forked type checker stops unexpectedly. If you have an idea, feel free to help us here. Any help is very much appreciated.
When running your app in AOT mode, formatting (mostly whitespace in form of newlines) is not preserved. That's because AST transformations alone are not enough and the AOT compiler runs a type check that will error due to missing properties, even though the properties were created on the AST. We are talking about properties that a decorator (ObservableEvent) will create at runtime. It's important to keep in mind that source files are immutable, this means any transformations are not reflected back to the actual text (sourceFile.getText()) of the source file. However, this is important and therefore the current solution uses a printer to get an updated text version of the transformed AST which we then store back into the internal source file cache. Even though the formatting is not preserved, we believe it's not a deal breaker and shouldn't stop you from using this library. Serving your app with AOT enabled shouldn't be the default development environment and it should only be used to test your app. You can still look at the source maps, add breakpoints and debug your application. So no real downsides other than missing new lines. Nevertheless, we are still trying to find a "better" solution to this inconvenience. If you have ideas, please check out this issue.
If you are already using a custom webpack configuration to adjust the behavior of the build process, it's recommended
to follow the manual installation guide instead of using the ng add schematic. We recommend to stick to ngx-build-plus as it's very convenient to work with and create a plugin that takes care of merging in your custom webpack config as well as the one provided by ngx-template-streams. Finally, you have to call our build plugin (you'll find this in @typebytes/ngx-template-streams/internal/plugin.js) to make sure the compiler plugin is correctly configured to allow template and AST transformations.
No, that's not necessary. The library will automatically complete and clean up every event stream for you when the component is destroyed.
If you are working with TypeScript's strict mode or have set strictPropertyInitialization to true in your tsconfig.json, you will experience an error using @ObservableEvent().
@ObservableEvent()
click$: Observable<MouseEvent>;
// ^ Property 'click$' has no initializer
// and is not definitely assigned in the constructor.
ngx-template-streams ensures that your observable streams are initialized.
That's why you can easily get around this using the non-null-assertion operator of TypeScript. 👍
@ObservableEvent()
- click$: Observable<MouseEvent>;
+ click$!: Observable<MouseEvent>;
ngx-template-streams will be maintained under the Semantic Versioning guidelines. Releases are numbered with the following format:
<MAJOR>.<MINOR>.<PATCH>
For more information on SemVer, please visit http://semver.org.
MIT License (MIT) © Dominic Elm and Kwinten Pisman
FAQs
[](https://github.com/prettier/prettier) [](https://www.npmjs.com/package/@typebytes/ngx-template-
We found that @typebytes/ngx-template-streams demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
CVE disclosures hit a record 48,185 in 2025, driven largely by vulnerabilities in third-party WordPress plugins.

Security News
Socket CEO Feross Aboukhadijeh joins Insecure Agents to discuss CVE remediation and why supply chain attacks require a different security approach.

Security News
Tailwind Labs laid off 75% of its engineering team after revenue dropped 80%, as LLMs redirect traffic away from documentation where developers discover paid products.