Angular / Basic / Lesson 2
Templates, binding, and events
Read a template as a view of component state, bind to the correct DOM target, and make user events explicit state transitions.
On this page
Lesson goals
- Distinguish interpolation, property binding, and attribute binding.
- Handle events without hiding domain rules in markup.
- Use semantic controls and understand template expression scope.
Bindings connect state to the DOM
Interpolation inserts a component expression into text, while property binding writes a value to a DOM or component property. Attribute binding targets an HTML attribute explicitly and is useful when no matching property is intended, including many ARIA attributes. A string that looks like a boolean is not the same as the boolean value expected by disabled. Angular evaluates template expressions in a defined component context, not as unrestricted application scripts. Keep expensive transformations and side effects outside display expressions so the view remains predictable when rendering checks repeat.
Test a state transition before connecting the button
The complete local model has a count and an increment operation. Calling the operation verifies its arithmetic without claiming a DOM event occurred. In an Angular template, an event binding such as (click) connects the browser event to a class operation. Use a native button for an action rather than adding click behavior to a noninteractive element. The domain operation should still validate its own bounds when the application requires them; disabling a button is presentation, not authorization or server-side validation. Keep model tests and real event tests as separate evidence.
export {};
class CounterModel {
count = 0;
increment(): void { this.count += 1; }
}
const model = new CounterModel();
model.increment();
console.log(model.count);
1
The count becomes one after one direct call. Browser dispatch, keyboard behavior, and Angular rendering are not exercised by this model.
Bind values and events at their intended targets
The contextual template assumes count, limit, and increment exist on its owning component. The disabled property receives a boolean expression, and the ARIA label receives text through attribute binding. The button type prevents accidental form submission if the component later appears inside a form. Interpolation renders the current count. Expected behavior is one increment per allowed activation, with a disabled presentation at the limit. This template is not independently runnable because no component fixture or browser host is provided. A keyboard test should activate the native button through its supported interaction.
<p>Count: {{ count }}</p>
<button type="button"
[disabled]="count >= limit"
[attr.aria-label]="'Increase count'"
(click)="increment()">
Increase
</button>
With count zero and limit three, the button is enabled and the text shows Count: 0. Actual event and rendering behavior requires the declared component and browser fixture.
Extract only the event data the operation needs
The event object is available as $event in the handler expression. Its static type depends on the event and target; a generic Event target is not automatically a particular input element. Prefer a template reference or a properly typed handler rather than an unchecked cast that assumes every target shape. Keep parsing and normalization explicit: an input text value is text even when it contains digits. Two-way binding combines a value flow and a change notification, but it does not replace validation. Avoid putting several unrelated updates into one long template expression that is difficult to test or explain.
Verify behavior and accessible meaning together
A working mouse click does not prove a control is keyboard-accessible or has an understandable name. Prefer native semantics, visible labels, and focus indicators. Test enabled and disabled states, boundary values, repeated activation, and any expected form behavior. When an attribute is optional, decide whether null should remove it rather than create misleading text. Do not interpolate untrusted HTML into a plan that assumes it becomes executable markup; Angular text interpolation treats it as text. Security and authorization still belong at real trust boundaries. A template should expose the featureโs state clearly without becoming the place where every business rule is hidden.
Common errors and fixes
A disabled binding receives text rather than a boolean.
- Why it happens
- String interpolation was used for a property contract.
- How to fix it
- Bind the boolean expression with property syntax.
- Prevent it next time
- Check the target property type before choosing binding syntax.
Clicking an action submits an enclosing form.
- Why it happens
- The native button type was not specified.
- How to fix it
- Use type="button" for non-submit actions.
- Prevent it next time
- Test controls inside their real form context.
A handler assumes every event target is a text input.
- Why it happens
- A broad event type was cast instead of checked or structured.
- How to fix it
- Use a template reference or a correctly typed handler boundary.
- Prevent it next time
- Keep event extraction separate from domain parsing.
Check your understanding
1. Extend CounterModel with a limit of three and reject increments at the limit. State the result after five calls and why the template disabled state alone is insufficient.
Show worked solution
Check count < limit inside increment before changing the value. Five calls then leave count at three. The disabled button guides user interaction but direct method calls, another component, or untrusted server requests can bypass that presentation. Test the model boundary independently and retain a component test that the disabled property matches the same rule.
2. An input contains "12" and its value is passed to a numeric domain operation. Describe the missing step and a useful negative test.
Show worked solution
Parse and validate the text according to the accepted numeric format, then check integer and range rules before the operation. A value containing "12x" should be rejected if the contract requires a complete integer. Typing the operation parameter number does not convert browser text or justify an assertion that the event target already provided valid domain data.
Key takeaways
- Choose bindings by the target contract.
- Treat events as explicit state transitions.
- Test native semantics and domain rules separately.