Bindings
Attributes, property bindings, events and two-way — the four Angular channels, kept apart.
Angular has four ways to pass something to a component, and they mean four different things. Blasdoc keeps them apart — in the parser, in the IR and at runtime — because collapsing them into one bag of props would destroy the semantics and make an accurate source rendering impossible.
The four channels
| Source | Meaning | Applied as |
|---|---|---|
variant="primary" | static attribute | an input when declared, a host attribute otherwise |
[variant]="tone" | property binding | the evaluated value, set as an input |
(clicked)="buy()" | event binding | a subscription to the output |
[(value)]="name" | two-way | input plus valueChange |
The context
Every expression is evaluated against one object — and only that object.
On a page, the class itself:
@Component({ selector: 'app-buy-page', templateMD, components: [AppButton] })
export class BuyPage {
count = 0;
locked = false;
buy(sku: string): void {
this.count++;
}
}Rendering a string instead? Hand the context in:
provideBlasdoc({
context: { count: 0, buy: (sku: string) => cart.add(sku) },
});<blasdoc-content [source]="markdown" [context]="context" />Static attributes
<app-button variant="primary">Comprar</app-button>When the component declares an input of that name, it is set as an input. When it does not, it lands on the host element as an attribute — the same split Angular applies in a template.
Property bindings
<app-button [disabled]="count === 0">Checkout</app-button><app-button [disabled]="count === 0" variant="primary">Checkout</app-button>The expression is re-evaluated whenever something in the document changes it.
Event bindings
<app-button (clicked)="bump()">Pressed {{ count }} times</app-button><app-button (clicked)="bump()" variant="primary">Pressed {{ count }} times</app-button>$event is available, and a statement may assign:
<app-button (clicked)="locked = !locked">Toggle</app-button>
<app-input (valueChange)="name = $event" />Two-way
<app-switch [(checked)]="locked" />A two-way binding requires a real …Change output. When the component does not
declare one, Blasdoc reports it rather than silently listening for an event that
will never fire.
Bindings on plain HTML
They are legitimate Angular, so they work here too — applied as attributes:
<div [title]="'Pressed ' + count + ' times'">Hover me</div>The expression language
Expressions are interpreted, never compiled. There is no eval and no
new Function: an expression is parsed into an AST and walked by an evaluator
that can reach your context object and the call's locals, and nothing else.
Supported:
paths user.name, items[0].title
calls buy('sku-1'), format(total, 2)
literals 'text', 42, true, null, [1, 2], { a: 1 }
operators + - * / % === !== < <= > >= && || ?? !
ternary count > 0 ? 'ready' : 'empty'
optional user?.profile?.name
assignment locked = !lockedNot supported: pipes, new, arrow functions, await, and anything that would
amount to running arbitrary code.
When the view refreshes
Re-evaluation is triggered by events inside the document. A context mutated from
outside it needs a new context reference, or a contextChange, to refresh.