Core Concepts
Angular integration
Bind rs-x expressions to Angular templates with the RsxPipe — reactive updates propagate automatically using Angular's change detection, with no manual subscriptions.
Practical value
Key points
- The pipe is impure (pure: false) — Angular checks it every change-detection cycle, but the pipe itself only recreates the expression when the expression string or context object changes.
- Pass an expression string and a context object: {{ "a + b" | rsx: model }}. Or pass a pre-built IExpression and omit the context: {{ expr | rsx }}.
- When the pipe owns the expression (string input), it disposes the expression on ngOnDestroy. When you pass a pre-built IExpression, the pipe only subscribes — you own the lifecycle.
- providexRsx() registers three providers: an APP_INITIALIZER that loads the rs-x module, IExpressionFactoryToken, and IExpressionChangeTransactionManagerToken.
- Use IExpressionChangeTransactionManagerToken to batch multiple model mutations into a single change notification — important for performance when updating many fields at once.
- Passing null or undefined to the pipe is safe — it renders nothing and cleans up any previous expression.
Setup — standalone app example
Register rs-x providers in your ApplicationConfig.
// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { providexRsx } from '@rs-x/angular';
export const appConfig: ApplicationConfig = {
providers: [
// ... other providers
...providexRsx(),
],
};Setup — NgModule app example
Import RsxPipe and spread providexRsx() into your providers array.
// app.module.ts (NgModule-based apps)
import { NgModule } from '@angular/core';
import { RsxPipe, providexRsx } from '@rs-x/angular';
@NgModule({
declarations: [AppComponent],
imports: [RsxPipe],
providers: [...providexRsx()],
bootstrap: [AppComponent],
})
export class AppModule {}RsxPipe — string expression example
Pass a model as the pipe argument. The pipe creates, owns, and disposes the expression. The template updates whenever any model field changes.
// component.ts
import { Component } from '@angular/core';
import { RsxPipe } from '@rs-x/angular';
@Component({
selector: 'app-greeting',
standalone: true,
imports: [RsxPipe],
template: `
<p>{{ 'firstName + " " + lastName' | rsx: model }}</p>
`,
})
export class GreetingComponent {
model = {
firstName: 'Jane',
lastName: 'Doe',
};
}
// Mutate the model — the template updates automatically
// this.model.firstName = 'Alice';RsxPipe — pre-built IExpression example
Build the expression once (e.g. in a class field or service) and pass it directly. The pipe subscribes but does not dispose on destroy.
// component.ts
import { Component } from '@angular/core';
import { rsx } from '@rs-x/expression-parser';
import { RsxPipe } from '@rs-x/angular';
const model = { price: 100, quantity: 3 };
@Component({
selector: 'app-order-total',
standalone: true,
imports: [RsxPipe],
template: `
<span>Total: {{ totalExpr | rsx }}</span>
`,
})
export class OrderTotalComponent {
// Build the expression once — share it across any number of templates
readonly totalExpr = rsx<number>('price * quantity')(model);
}
// Update the model anywhere — all bound templates re-render
model.quantity = 5;RsxPipe — async values example
Async model fields (Promise, Observable) work transparently — no async pipe needed. The template updates whenever the Observable emits.
// component.ts
import { Component } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { RsxPipe } from '@rs-x/angular';
@Component({
selector: 'app-live-price',
standalone: true,
imports: [RsxPipe],
template: `
<p>Price (inc. tax): {{ 'base * (1 + taxRate)' | rsx: model }}</p>
`,
})
export class LivePriceComponent {
model = {
base: new BehaviorSubject(100),
taxRate: 0.21,
};
updatePrice(newBase: number) {
this.model.base.next(newBase); // template updates automatically
}RsxPipe — null safety example
Passing null or undefined is safe and renders as an empty string.
// Passing null or undefined is safe — the pipe renders nothing
// and does not create or hold an expression.
@Component({
template: `
<span>{{ maybeExpr | rsx }}</span>
`,
})
export class SafeComponent {
maybeExpr: string | null = null; // renders as empty string
}Manual injection — factory and transaction manager example
Inject IExpressionFactoryToken and IExpressionChangeTransactionManagerToken directly to build expressions in services or use batched updates.
import { inject, Component } from '@angular/core';
import {
IExpressionFactoryToken,
IExpressionChangeTransactionManagerToken,
} from '@rs-x/angular';
@Component({ selector: 'app-manual', standalone: true, template: '' })
export class ManualComponent {
private readonly factory = inject(IExpressionFactoryToken);
private readonly txManager = inject(IExpressionChangeTransactionManagerToken);
ngOnInit() {
const model = { a: 1, b: 2 };
const expr = this.factory.create<number>(model, 'a + b');
// Batch multiple model mutations into a single change notification
this.txManager.begin();
model.a = 10;
model.b = 20;
this.txManager.commit(); // expr fires changed once, not twice
}
}Installation example
Install all required packages.
npm install @rs-x/core @rs-x/state-manager @rs-x/expression-parser @rs-x/angular