Directive Guides
Directives are easier to learn by role than by name. Start here when you want
to understand how AngularTS extends HTML, then use the
Directives reference for individual
attributes and API details.
Learning Path
- Overview: how directives are
discovered, compiled, linked, and prioritized.
- Data Binding: directives
that synchronize scope data with text, HTML, classes, styles, and form
controls.
- Structural Directives:
directives that add, remove, repeat, switch, include, or protect DOM
sections.
- Forms: validation, model
options, form state, and messages.
- HTTP Directives: declarative
requests and Server-Sent Events from HTML.
- Animation Directives: CSS
hooks and animation-specific attributes.
- Advanced Directives: workers,
WebAssembly, channels, DOM references, accessibility, and event helpers.
Reference Lookup
When you already know the directive name, use the
per-directive reference. The reference
keeps pages small and specific, while this section keeps the conceptual grouping
that helps new users build a mental model.
1 - Advanced directives: workers, WASM, channels, viewport
AngularTS advanced directives for Web Workers, WebAssembly, lazy loading, event channels, DOM references, and accessibility — all declarative in HTML.
AngularTS ships several advanced directives that expose modern browser APIs — Web Workers, WebAssembly, Intersection Observer, and custom event channels — directly in HTML without requiring JavaScript. These directives are registered as part of the core ng module and are available in every AngularTS application.
Controller and scope directives
ng-controller
Attaches a controller function to a section of the DOM, creating a new child scope populated by the controller.
<p>{{ ctrl.title }}</p>
</div>
.controller('TodoController', function($scope) {
$scope.title = 'My Todos';
});
ng-scope
Marks an explicit scope boundary. Useful for isolating a section of the page without a controller.
<p>Hello {{ user.name }}</p>
</div>
ng-init
Initializes scope variables inline in the template. Best suited for simple one-off values or prototyping.
Hello {{ name }}, count is {{ count }}
</div>
Tip: Prefer controllers or services over ng-init for any real application logic. ng-init is ideal for zero-JS demos and quick prototypes.
DOM reference directives
ng-el
Use ng-el when you only need the native DOM element. It is the simple DOM
reference API and is usually the clearest choice for canvas, focus management,
layout measurement, and pointer-driven UI.
<section ng-controller="BoardController as $ctrl">
<canvas ng-el="$ctrl.boardEl"></canvas>
</section>
function BoardController() {
this.boardEl = null;
}
ng-el="$ctrl.boardEl" stores the element on the controller. Use this form
with controller-as syntax.
For simple scope shorthand, pass a bare name:
<canvas ng-el="boardEl"></canvas>
If the value is omitted, AngularTS uses the element id.
<canvas id="board" ng-el></canvas>
ng-ref
Use ng-ref when you need a component or directive controller reference, or
when you need to assign the reference to a full expression such as
$ctrl.child.
<search-box ng-ref="$ctrl.search"></search-box>
<button ng-click="$ctrl.search.clear()">Clear</button>
By default, ng-ref reads the component or element-directive controller when
one exists, and falls back to the DOM element.
ng-ref-read is not a standalone directive. It only changes what ng-ref
assigns.
<canvas ng-ref="$ctrl.boardEl" ng-ref-read="$element"></canvas>
<my-widget ng-ref="$ctrl.resizeHandle" ng-ref-read="resizeHandle"></my-widget>
Use ng-ref-read="$element" only when you specifically need the DOM element
through an assignable expression. For the common DOM-only case, prefer ng-el.
function BoardController() {
this.boardEl = null;
}
Event and communication directives
ng-channel
Subscribes to a named event channel (backed by $eventBus). When an event is published on the channel, the expression is evaluated.
Latest: {{ lastNotification }}
</div>
ng-listener
Attaches a DOM event listener to the element using the native addEventListener API — without the overhead of Angular’s event directive wiring.
Scroll-tracked content
</div>
ng-inject
Injects a named service directly into the scope, making it accessible in the template without a controller.
<!-- $http is now available in this scope -->
</div>
ng-viewport
Uses the browser’s Intersection Observer API to defer content rendering until the element enters the viewport. Ideal for below-the-fold content.
<!-- loadComments() is called once when this enters the viewport -->
<div ng-repeat="comment in comments">{{ comment.text }}</div>
</div>
ng-viewport
Expression to evaluate when the element enters the viewport. Called once and not repeated.
ng-cloak
Prevents the browser from briefly displaying uncompiled template syntax ({{ }}) before AngularTS bootstraps. Add to the root element or any element with interpolation.
<div ng-cloak>
<p>Hello {{ user.name }}</p>
</div>
Note: Always include the CSS rule [ng-cloak] { display: none; } in your stylesheet when using ng-cloak.
Web Workers and WebAssembly
ng-worker
Runs a JavaScript file in a Web Worker and binds the result back to scope. The worker communicates via postMessage.
data-params="{ items: largeList }"
data-on-result="sortedItems = $result">
<div ng-repeat="item in sortedItems">{{ item.name }}</div>
</div>
ng-worker
- Type:
string - Required: yes
Path to the Web Worker JavaScript file.
data-params
Data to pass to the worker as the message payload.
data-on-result
Expression evaluated when the worker posts a result. $result contains the worker’s response.
interval
Re-send the params to the worker on a millisecond interval.
throttle
Throttle worker invocations to at most once per N milliseconds.
The worker file uses standard onmessage / postMessage:
var sorted = e.data.items.slice().sort((a, b) => a.name.localeCompare(b.name));
postMessage(sorted);
};
ng-wasm
Loads a WebAssembly module and makes its exports available on scope.
<p>Result: {{ math.add(3, 4) }}</p>
</div>
ng-wasm
- Type:
string - Required: yes
Path to the .wasm file to load.
as
Scope property name to assign the WASM exports object to. Defaults to wasm.
Transclusion
ng-transclude
Used inside custom directive templates to mark where transcluded content should be inserted.
return {
transclude: true,
template: '<div class="panel"><ng-transclude></ng-transclude></div>'
};
});
<p>This content is transcluded into the panel.</p>
</my-panel>
Non-bindable sections
ng-non-bindable
Prevents AngularTS from compiling or interpolating a subtree. Use this for displaying raw Angular syntax as documentation or code examples.
{{ this will not be interpolated }}
ng-repeat="item in items"
</pre>
Aria directives
AngularTS automatically manages ARIA attributes for accessibility. These directives are applied alongside their functional counterparts:
| Directive | ARIA attribute managed |
|---|
ng-disabled | aria-disabled |
ng-checked | aria-checked |
ng-readonly | aria-readonly |
ng-required | aria-required |
ng-show / ng-hide | aria-hidden |
ng-model | aria-value*, role |
The $aria service and $ariaProvider let you configure which ARIA attributes are automatically managed:
$ariaProvider.config({
ariaDisabled: true,
ariaChecked: true,
ariaReadonly: true,
ariaRequired: true,
ariaHidden: true,
ariaValue: true,
tabindex: true
});
});
Event directives
AngularTS generates event directives for all common DOM events. They all follow the ng-<eventname>="expression" pattern:
| Directive | DOM event |
|---|
ng-click | click |
ng-dblclick | dblclick |
ng-focus | focus |
ng-blur | blur |
ng-keydown | keydown |
ng-keyup | keyup |
ng-keypress | keypress |
ng-mouseenter | mouseenter |
ng-mouseleave | mouseleave |
ng-mousemove | mousemove |
ng-mouseover | mouseover |
ng-mousedown | mousedown |
ng-mouseup | mouseup |
ng-submit | submit (on forms) |
ng-cut | cut |
ng-copy | copy |
ng-paste | paste |
ng-load | load |
ng-on | custom event name |
The expression is evaluated in the current scope. $event refers to the native DOM event object:
<input ng-keydown="$event.key === 'Enter' && submit()">
Event policy can be declared once on the same element and applies to every ng-on-* or generated event directive on that element:
<div
ng-on-pointerdown="$ctrl.startDrag($event)"
data-event-prevent
data-event-capture>
</div>
Supported policy attributes:
| Attribute | Behavior |
|---|
data-event-prevent | Calls $event.preventDefault() first |
data-event-stop | Calls $event.stopPropagation() first |
data-event-capture | Registers the listener with capture: true |
data-event-once | Registers the listener with once: true |
data-event-passive | Registers the listener with passive: true |
data-event-passive cannot be combined with data-event-prevent, because passive listeners cannot cancel default browser behavior.
ng-pointer-capture can be combined with ng-on-pointer* handlers for game and board interactions that need reliable pointer streams:
<div
ng-pointer-capture
ng-on-pointerdown="$ctrl.startDrag($event)"
ng-on-pointermove="$ctrl.drag($event)"
ng-on-pointerup="$ctrl.drop($event)"
data-event-prevent>
</div>
2 - Animation Directives: ng-animate-swap and CSS Hooks
Use ng-animate-swap, ng-animate-children, and CSS class hooks to animate structural directives like ng-if, ng-repeat, and ng-show with the $animate service.
AngularTS integrates animations into the same lifecycle that drives structural directives. Rather than managing setTimeout calls or CSS transitions manually, you add the animate attribute to an element and AngularTS’s $animate service handles entry, exit, and class-change transitions — applying and removing CSS hook classes at the right moment in the digest cycle.
How $animate integrates with directives
Structural directives — ng-if, ng-repeat, ng-show, ng-hide, ng-switch, ng-include, and ng-animate-swap — all check whether an element carries animation data before manipulating the DOM. When the animate attribute is present on an element (detected via hasAnimate(element)), they delegate DOM operations to $animate instead of performing them directly:
| Operation | Without $animate | With $animate |
|---|
| Insert element | element.after(clone) | $animate.enter(clone, parent, after) |
| Remove element | element.remove() | $animate.leave(element) |
| Toggle class | element.classList.add(cls) | $animate.addClass(element, cls) |
| Swap classes | el.add(a); el.remove(b) | $animate.setClass(element, add, remove) |
This means you can add CSS transitions to any structural operation simply by adding the animate attribute and writing the corresponding CSS rules.
CSS animation hook classes
When $animate performs an operation it applies a sequence of CSS classes in two frames to give the browser time to set up the transition:
Element enter
ng-enter → ng-enter + ng-enter-active → (classes removed)
Element leave
ng-leave → ng-leave + ng-leave-active → element removed
Element move (ng-repeat reorder)
ng-move → ng-move + ng-move-active → (classes removed)
Class add/remove
ng-CLASS-add → ng-CLASS-add + ng-CLASS-add-active → CLASS applied
ng-CLASS-remove → ng-CLASS-remove + ng-CLASS-remove-active → CLASS removed
.card.ng-enter {
opacity: 0;
transform: translateY(-8px);
transition: opacity 0.25s ease, transform 0.25s ease;
}
.card.ng-enter-active {
opacity: 1;
transform: translateY(0);
}
/* Fade out when the element is removed */
.card.ng-leave {
opacity: 1;
transition: opacity 0.2s ease;
}
.card.ng-leave-active {
opacity: 0;
}
ng-animate-swap
ng-animate-swap swaps between transcluded content blocks using enter/leave animations as a watched expression changes. It runs at priority 550 — after ng-if (600) but before most attribute directives.
<div ng-animate-swap="currentAlert">
<div ng-if="currentAlert === 'success'" class="banner banner-success">
{{ message }}
</div>
<div ng-if="currentAlert === 'error'" class="banner banner-error">
{{ message }}
</div>
</div>
Each time the watched expression changes, the old element is removed via $animate.leave and the new transcluded clone is inserted via $animate.enter. The previous scope is destroyed before the new clone is linked.
A more common pattern is using ng-animate-swap to animate between stateful view components:
<div class="tab-content" ng-animate-swap="activeTab">
<div class="tab-panel" ng-include="activeTab + '.html'"></div>
</div>
opacity: 0;
transform: translateX(20px);
transition: opacity 0.3s ease, transform 0.3s ease;
}
.tab-panel.ng-enter-active {
opacity: 1;
transform: translateX(0);
}
.tab-panel.ng-leave {
opacity: 1;
transition: opacity 0.2s ease;
}
.tab-panel.ng-leave-active {
opacity: 0;
}
ng-animate-swap
The watched expression. Any time its value changes, the current transcluded block is removed with a leave animation and a new clone is entered. Use the for attribute alias interchangeably.
ng-animate-children
ng-animate-children propagates an animation-children flag to the element’s cache so that the $animate queue knows whether child elements should also be animated during parent transitions.
<div ng-if="showPanel" ng-animate-children="true">
<div class="panel-header">Header</div>
<div class="panel-body" ng-repeat="item in items">{{ item }}</div>
</div>
When ng-animate-children is set to "on" or "true" (or is present as an empty attribute), child animations run in parallel with the parent. Without it, the animation queue suppresses child animations while the parent is entering or leaving.
<ul ng-repeat="group in groups" ng-animate-children>
<li ng-repeat="item in group.items" class="list-item">{{ item }}</li>
</ul>
ng-animate-children
Accepts "on", "true", or an empty attribute to enable child animations. Any other value (or an interpolated expression evaluating to a falsy string) disables child animations.
Animating ng-repeat lists
ng-repeat triggers ng-enter and ng-leave animations for items added to or removed from the collection. Add the animate attribute on the repeated element to opt in.
<li class="task-item"
ng-repeat="task in tasks track by task.id"
animate>
<span ng-bind="task.title"></span>
<button ng-click="removeTask(task)">Remove</button>
</li>
</ul>
animation: slideIn 0.25s ease forwards;
}
.task-item.ng-leave {
animation: slideOut 0.2s ease forwards;
}
.task-item.ng-move {
transition: all 0.3s ease;
}
@keyframes slideIn {
from { opacity: 0; transform: translateX(-16px); }
to { opacity: 1; transform: translateX(0); }
}
@keyframes slideOut {
from { opacity: 1; transform: translateX(0); }
to { opacity: 0; transform: translateX(16px); }
}
.controller('TaskCtrl', function($scope) {
$scope.tasks = [
{ id: 1, title: 'Design wireframes' },
{ id: 2, title: 'Write tests' },
{ id: 3, title: 'Deploy to staging' }
];
$scope.removeTask = function(task) {
const idx = $scope.tasks.indexOf(task);
if (idx !== -1) $scope.tasks.splice(idx, 1);
};
});
Animating ng-show / ng-hide
ng-show and ng-hide both apply a temporary ng-hide-animate class alongside ng-hide when the $animate service is active. This class serves as the transition anchor:
transition: opacity 0.3s ease, max-height 0.3s ease;
overflow: hidden;
}
/* State when hidden */
.notification.ng-hide {
opacity: 0;
max-height: 0;
}
/* Applied during the animation frame to enable the transition */
.notification.ng-hide-animate {
display: block !important;
}
JavaScript animations via $animate
The $animateJs service allows you to register JavaScript-based animation hooks alongside CSS animations:
.animation('.flip-card', function() {
return {
enter: function(element, done) {
// Use Web Animations API or any library
element.animate(
[{ transform: 'rotateY(90deg)' }, { transform: 'rotateY(0deg)' }],
{ duration: 300, easing: 'ease-out' }
).onfinish = done;
return function(cancelled) {
if (cancelled) element.style.transform = '';
};
},
leave: function(element, done) {
element.animate(
[{ transform: 'rotateY(0deg)' }, { transform: 'rotateY(-90deg)' }],
{ duration: 250, easing: 'ease-in' }
).onfinish = done;
}
};
});
{{ card.content }}
</div>
Tip: JavaScript animations and CSS animations can coexist on the same element. The $animate service runs CSS animations and JavaScript animation hooks in parallel, calling done only after both complete.
Animation and the HTTP directives
The HTTP directives (ng-get, ng-post, etc.) also support the animate attribute. When present, swapped content uses $animate.enter and $animate.leave instead of direct DOM manipulation:
trigger="load"
swap="innerHTML"
animate
class="post-content">
</div>
opacity: 0;
transition: opacity 0.4s ease;
}
.post-content.ng-enter-active {
opacity: 1;
}
3 - Data Binding Directives in AngularTS Explained
Explore ng-bind, ng-bind-html, ng-model, ng-class, ng-style, and ng-init with real examples covering one-way and two-way data binding.
Data binding directives synchronise values between the scope and the DOM without requiring you to write event listeners or DOM queries by hand. AngularTS provides one-way and two-way binding variants to cover every use case, from safe text output to full form synchronisation.
ng-bind
ng-bind is the attribute equivalent of double-curly interpolation ({{ }}). It watches the given scope expression and writes the result as the element’s textContent. Because it sets textContent, it is immune to XSS — no HTML is ever parsed.
<p>Hello, {{ user.name }}!</p>
<!-- Using ng-bind — element is empty until AngularTS compiles -->
<p>Hello, <span ng-bind="user.name"></span>!</p>
The implementation watches the expression on every digest. The lazy attribute defers the first watch until a digest is explicitly triggered:
<span ng-bind="heavyExpression" lazy></span>
Tip: Prefer ng-bind over {{ }} in the <body> of server-rendered pages to avoid the flash of uncompiled template (FOUC). The element starts empty and is filled when Angular bootstraps.
Parameters
ng-bind
- Type:
expression - Required: yes
Any AngularTS expression. The result is converted to a string via stringify() and written to element.textContent. null and undefined render as an empty string.
ng-bind-template
ng-bind-template lets you embed multiple interpolated expressions inside a single attribute value — useful when the surrounding text cannot hold a child element.
<title ng-bind-template="{{pageTitle}} — {{siteName}}"></title>
<!-- Equivalent using child elements (not possible inside <title>) -->
The directive uses attr.$observe to watch the already-interpolated string; Angular re-evaluates the interpolation on each digest and writes the result to textContent.
ng-bind-template
- Type:
string with {{ }} expressions - Required: yes
A string literal containing one or more {{ expression }} placeholders. The entire interpolated string is written to textContent.
ng-bind-html
ng-bind-html inserts the expression result directly into element.innerHTML. Use this directive when you need to render server-provided or pre-sanitised markup. It calls $parse during compilation to check for interpolation errors, then watches the expression and sets innerHTML on every change.
<div ng-bind-html="article.body"></div>
.controller('ArticleCtrl', function($scope) {
$scope.article = {
body: '<p>Hello <strong>world</strong></p>'
};
});
Warning: ng-bind-html sets innerHTML directly. Always sanitise content on the server before placing it in scope. Avoid interpolating raw user input with this directive.
ng-bind-html
- Type:
expression - Required: yes
An expression evaluating to a string of HTML. The string is written to element.innerHTML. null and undefined clear the element.
ng-model
ng-model establishes a two-way binding between a form input and a scope property. Changes to the input update the scope; changes to the scope property update the input. It is the backbone of AngularTS form handling.
Text input
<input type="text" ng-model="user.name" />
<p>Hello, {{ user.name }}!</p>
Checkbox
<input type="checkbox" ng-model="settings.notifications" />
<p ng-bind="settings.notifications ? 'On' : 'Off'"></p>
Select
<select ng-model="selected.country">
<option value="us">United States</option>
<option value="gb">United Kingdom</option>
</select>
<p>You selected: {{ selected.country }}</p>
Textarea
<textarea ng-model="message.body" rows="4"></textarea>
<p>Characters: {{ message.body.length }}</p>
ng-model creates an NgModelController instance that manages:
$viewValue — the formatted value shown in the control (always a string for native inputs)$modelValue — the parsed value stored in the scope$parsers — pipeline from view to model (view → model transformation)$formatters — pipeline from model to view (model → view transformation)$validators — synchronous validator functions keyed by error name$asyncValidators — async validator functions that return Promises$error — object whose keys are failing validator names
State properties
| Property | Type | Description |
|---|
$pristine | boolean | true if the user has not interacted with this control |
$dirty | boolean | true after the user has changed the value |
$touched | boolean | true after the control has lost focus |
$untouched | boolean | true before the control has ever been blurred |
$valid | boolean | true if all validators pass |
$invalid | boolean | true if any validator fails |
CSS classes
AngularTS automatically toggles CSS classes on the element:
.ng-valid { border-color: green; }
/* Applied when the model is invalid */
.ng-invalid { border-color: red; }
/* Applied when the user has not yet changed the value */
.ng-pristine { }
/* Applied once the user has changed the value */
.ng-dirty { }
/* Applied once the input has been blurred */
.ng-touched { }
ng-model
- Type:
expression - Required: yes
An assignable AngularTS expression. The expression must be assignable (pointing to a scope property), otherwise AngularTS throws an ngModel:nonassign error.
ng-class
ng-class conditionally adds and removes CSS classes based on a scope expression. It supports three input formats: an object, an array, or a string.
Object syntax
Keys are class names; values are expressions. A class is applied when its value is truthy.
<div ng-class="{ active: isActive, 'text-danger': hasError }">
Status panel
</div>
$scope.isActive = true;
$scope.hasError = false;
// Result: class="active"
For larger views, prefer projecting domain state into a view-model class map and binding that map directly. This keeps templates readable and lets JavaScript type checking validate the object shape.
<button ng-class="tile.classes">
Fire
</button>
/**
* @param {{ state: string, sunk: boolean }} tile
* @returns {ng.ClassMap}
*/
function tileClasses(tile) {
return {
placed: tile.state === 'unit',
hit: tile.state === 'hit',
miss: tile.state === 'miss',
sunk: tile.sunk,
};
}
$scope.tile = {
state: 'unit',
sunk: false,
classes: tileClasses({ state: 'unit', sunk: false }),
};
Array syntax
Each element is evaluated as a class name or object. Useful when combining static and conditional classes.
<div ng-class="[baseClass, { highlighted: isSelected }]">
Item
</div>
$scope.baseClass = 'card';
$scope.isSelected = true;
// Result: class="card highlighted"
String syntax
The expression evaluates to a space-separated class string.
<div ng-class="currentTheme">
Themed content
</div>
$scope.currentTheme = 'theme-dark compact';
// Result: class="theme-dark compact"
The implementation uses reference counting internally (digestClassCounts) so that when multiple ng-class directives compete on the same element, classes are not prematurely removed while still in use.
ng-class-even / ng-class-odd
These variants apply classes only to even- or odd-indexed rows inside an ng-repeat.
<li ng-repeat="item in items"
ng-class-even="'row-even'"
ng-class-odd="'row-odd'">
{{ item.name }}
</li>
</ul>
ng-class
- Type:
ng.ClassValue (string | ng.ClassMap | array) - Required: yes
- String — a space-delimited list of class names.
- Object — keys are class names, values are truthy/falsy conditions.
- Array — each element is recursively processed using the above rules.
ng-style
ng-style watches a scope object and applies its key-value pairs as inline CSS properties using element.style.setProperty. When the expression changes, the old properties are first removed before the new ones are applied.
<div ng-style="boxStyles">
Dynamic box
</div>
'background-color': '#4f46e5',
'color': '#ffffff',
'padding': '16px',
'border-radius': '8px'
};
<p ng-style="{ 'font-size': fontSize + 'px', 'font-weight': isBold ? 'bold' : 'normal' }">
Styled text
</p>
Note: CSS property names must use hyphen-case (background-color, not backgroundColor) since the implementation calls element.style.setProperty rather than assigning to element.style[key].
ng-style
- Type:
object - Required: yes
An expression evaluating to an object where keys are CSS property names in hyphen-case and values are CSS value strings. Setting the expression to null or undefined removes all previously applied inline styles.
ng-init
ng-init evaluates an expression during the pre-link phase — before child directives are linked. It is most useful for initialising scope variables inline without requiring a separate controller.
<div ng-init="count = 0; title = 'Hello'">
<button ng-click="count = count + 1">Clicked {{ count }} times</button>
</div>
<ul>
<li ng-repeat="item in items" ng-init="pos = $index + 1">
{{ pos }}. {{ item.name }}
</li>
</ul>
The implementation checks whether a controller is present on the element (via getController) and, if so, evaluates the expression on the controller’s scope. Otherwise it falls back to the current scope.
Warning: Use ng-init sparingly. For any non-trivial initialisation, use a controller instead. Keeping logic in templates makes it harder to test and maintain.
ng-init
- Type:
expression - Required: yes
An AngularTS expression or semicolon-separated list of expressions evaluated once in the pre-link phase. The expression has access to the current scope.
4 - Form Directives and Validation in AngularTS
Complete guide to AngularTS form directives: ng-model, built-in validators, ng-messages, ng-model-options, and form state properties like $valid and $dirty.
AngularTS treats HTML forms as first-class citizens of the framework. Every <form> element (and ng-form directive) creates a FormController instance that tracks the aggregate validity and dirty state of all its inputs. Each input bound with ng-model gets its own NgModelController that manages the view-to-model pipeline, validation, and CSS class hooks.
A plain <form> element is enhanced by AngularTS into a FormController. Give the form a name attribute and the controller is published on the current scope under that name, giving you programmatic access to validation state.
<input type="email" name="email"
ng-model="user.email"
required />
<button type="submit"
ng-disabled="registrationForm.$invalid">
Register
</button>
</form>
.controller('RegistrationCtrl', function($scope) {
$scope.register = function() {
if ($scope.registrationForm.$valid) {
// submit logic
}
};
});
| Property | Type | Description |
|---|
$valid | boolean | true when all child controls are valid |
$invalid | boolean | true when any child control is invalid |
$pristine | boolean | true before any control has been changed |
$dirty | boolean | true after any control has been changed |
$submitted | boolean | true after the form’s submit event fires |
$error | object | Keys are validator names; values are arrays of failing controls |
$pending | object | Keys are validator names with pending async validators |
AngularTS automatically toggles these classes on both the <form> element and each ng-model input:
| Class | Applied when |
|---|
ng-pristine | Control has not been changed |
ng-dirty | Control has been changed |
ng-valid | All validators pass |
ng-invalid | Any validator fails |
ng-submitted | Form has been submitted |
ng-touched | Input has been blurred at least once |
ng-untouched | Input has never been blurred |
ng-pending | An async validator is in progress |
input.ng-invalid.ng-dirty {
border: 2px solid #ef4444;
}
input.ng-valid.ng-dirty {
border: 2px solid #22c55e;
}
/* Show error messages only after the form is submitted or field is touched */
.field-error {
display: none;
}
input.ng-touched.ng-invalid ~ .field-error {
display: block;
}
ng-form creates a nested FormController that tracks its own controls independently. Because browsers do not allow nested <form> elements, ng-form is the standard way to group controls into sub-sections with their own validity state.
<!-- Shipping address group -->
<ng-form name="shippingForm">
<input name="street" ng-model="shipping.street" required />
<input name="city" ng-model="shipping.city" required />
<p ng-if="shippingForm.$invalid">Please complete shipping address.</p>
</ng-form>
<!-- Billing address group -->
<ng-form name="billingForm">
<input name="street" ng-model="billing.street" required />
<p ng-if="billingForm.$invalid">Please complete billing address.</p>
</ng-form>
<button ng-disabled="outerForm.$invalid">Submit</button>
</form>
The outer form’s $valid is false if any nested ng-form is invalid, allowing you to check a single top-level form while still presenting granular errors per section.
All standard HTML input types are supported. For <input>, <textarea>, and <select> elements, AngularTS registers type-specific parsers and validators automatically when ng-model is present.
Text / Email / URL
<input type="text"
name="username"
ng-model="user.username"
required
minlength="3"
maxlength="20"
ng-pattern="/^[a-z0-9_]+$/" />
<input type="email"
name="email"
ng-model="user.email"
required />
<input type="url"
name="website"
ng-model="user.website" />
Number
<input type="number"
name="age"
ng-model="user.age"
min="18"
max="120"
required />
Parses the input into a JavaScript number. Sets $error.number if the value is not numeric, $error.min or $error.max if out of range.
Checkbox
<!-- Simple boolean checkbox -->
<input type="checkbox"
name="agree"
ng-model="user.agreedToTerms"
required />
<!-- Custom true/false values -->
<input type="checkbox"
ng-model="settings.mode"
ng-true-value="'dark'"
ng-false-value="'light'" />
Radio
<label>
<input type="radio" ng-model="plan" value="free" /> Free
</label>
<label>
<input type="radio" ng-model="plan" value="pro" /> Pro
</label>
<label>
<input type="radio" ng-model="plan" value="enterprise" /> Enterprise
</label>
<p>Selected: {{ plan }}</p>
Select
<!-- Simple string options -->
<select name="country" ng-model="user.country" required>
<option value="">-- Choose a country --</option>
<option value="us">United States</option>
<option value="gb">United Kingdom</option>
</select>
<!-- Object options with ng-options -->
<select ng-model="selectedRole"
ng-options="role.id as role.label for role in roles">
<option value="">-- Select a role --</option>
</select>
Built-in validators
required / ng-required
required makes the field mandatory. ng-required accepts an expression so you can make a field conditionally required.
<!-- Conditionally required -->
<input type="text"
ng-model="user.company"
ng-required="user.accountType === 'business'" />
The validator calls NgModelController.$isEmpty on the view value. For text inputs, empty means "". For checkboxes, empty means false.
minlength / ng-minlength
ng-model="user.password"
ng-minlength="8" />
<p ng-if="form.password.$error.minlength">
Password must be at least 8 characters.
</p>
maxlength / ng-maxlength
ng-model="tweet.text"
ng-maxlength="280" />
<p>{{ 280 - tweet.text.length }} characters remaining</p>
pattern / ng-pattern
pattern accepts a regex literal (as an attribute value subject to interpolation). ng-pattern accepts a scope expression that evaluates to a RegExp object or a string.
<input type="text"
ng-model="user.phone"
pattern="^\d{10}$" />
<!-- Dynamic pattern from scope -->
<input type="text"
ng-model="user.code"
ng-pattern="validationRules.codePattern" />
codePattern: /^[A-Z]{2}\d{4}$/
};
Note: Avoid the g (global) flag on patterns used with ng-pattern. RegExp objects with the g flag maintain internal state between calls to .test(), which causes alternating pass/fail results.
ng-messages
ng-messages simplifies displaying validation error messages. It watches a form control’s $error object and renders the first matching ng-message child (or all matching, with the multiple attribute).
<div class="field">
<label>Email</label>
<input type="email"
name="email"
ng-model="user.email"
required />
<div ng-messages="signupForm.email.$error" role="alert">
<p ng-message="required">Email address is required.</p>
<p ng-message="email">Please enter a valid email address.</p>
</div>
</div>
</form>
By default, only the first matching error is shown. Add the multiple attribute to show all failing validators at once:
<p ng-message="required">Password is required.</p>
<p ng-message="minlength">At least 8 characters required.</p>
<p ng-message="pattern">Must contain a number and special character.</p>
</div>
ng-message-default
ng-message-default acts as a fallback shown when none of the specific ng-message keys match but there is at least one truthy error:
<p ng-message="pattern">Invalid format.</p>
<p ng-message-default>This field has an error.</p>
</div>
Reusable message templates
Use ng-messages-include to load a shared message template file:
<div ng-messages-include="'/partials/common-messages.html'"></div>
<p ng-message="email">Not a valid email address.</p>
</div>
ng-model-options
ng-model-options controls when the model is updated and how updates are debounced. It can be placed on a form (to apply to all controls) or on individual inputs.
Debounce
Delay model updates by a given number of milliseconds after the user stops typing:
<input type="text"
ng-model="search.query"
ng-model-options="{ debounce: 300 }" />
This fires a search 300ms after the last keystroke rather than on every character.
Update on blur
Only update the model when the input loses focus:
<input type="text"
ng-model="user.username"
ng-model-options="{ updateOn: 'blur' }" />
Combined events
Update immediately on blur, but also debounced on default (which covers all other events):
<input type="text"
ng-model="user.slug"
ng-model-options="{
updateOn: 'blur default',
debounce: { 'default': 500, 'blur': 0 }
}" />
Getter/setter
When getterSetter: true, the ng-model expression is expected to be a function that both gets and sets the value:
<input type="text"
ng-model="user.getName"
ng-model-options="{ getterSetter: true }" />
let _name = 'Alice';
$scope.user = {
getName: function(newValue) {
if (arguments.length) {
_name = newValue.trim();
}
return _name;
}
};
Available options
updateOn
Space-separated DOM event names that trigger model updates. Use "default" as a placeholder for the control’s standard update event (e.g., "input" for text inputs).
debounce
Milliseconds to wait before committing the view value. Can be an object mapping event names to delays: { 'blur': 0, 'default': 500 }.
allowInvalid
When true, the model is updated even when the value is invalid (normally the model is set to undefined when validation fails).
getterSetter
When true, the ng-model expression is treated as a function that acts as both getter and setter.
timezone
Timezone offset for date/time input parsing, e.g. "UTC" or "+0530".
The following example demonstrates a registration form with multiple input types, validators, debouncing, and ng-messages error display.
<!-- Username -->
<div class="field">
<label for="username">Username</label>
<input id="username"
type="text"
name="username"
ng-model="form.username"
ng-model-options="{ debounce: 300 }"
required
ng-minlength="3"
ng-maxlength="20"
ng-pattern="/^[a-z0-9_]+$/" />
<div ng-messages="regForm.username.$error"
ng-if="regForm.username.$touched || regForm.$submitted">
<p ng-message="required">Username is required.</p>
<p ng-message="minlength">At least 3 characters.</p>
<p ng-message="maxlength">No more than 20 characters.</p>
<p ng-message="pattern">Only lowercase letters, numbers, and underscores.</p>
</div>
</div>
<!-- Email -->
<div class="field">
<label for="email">Email</label>
<input id="email"
type="email"
name="email"
ng-model="form.email"
required />
<div ng-messages="regForm.email.$error"
ng-if="regForm.email.$touched || regForm.$submitted">
<p ng-message="required">Email is required.</p>
<p ng-message="email">Enter a valid email address.</p>
</div>
</div>
<!-- Password -->
<div class="field">
<label for="password">Password</label>
<input id="password"
type="password"
name="password"
ng-model="form.password"
required
ng-minlength="8" />
<div ng-messages="regForm.password.$error"
ng-if="regForm.password.$dirty">
<p ng-message="required">Password is required.</p>
<p ng-message="minlength">At least 8 characters required.</p>
</div>
</div>
<!-- Terms acceptance -->
<div class="field">
<label>
<input type="checkbox"
name="terms"
ng-model="form.agreedToTerms"
required />
I agree to the terms and conditions
</label>
<p ng-if="regForm.terms.$error.required && regForm.$submitted">
You must accept the terms.
</p>
</div>
<button type="submit"
ng-disabled="regForm.$invalid && regForm.$submitted">
Create account
</button>
</form>
.controller('RegistrationCtrl', function($scope) {
$scope.form = {};
$scope.submitRegistration = function() {
$scope.regForm.$setSubmitted();
if ($scope.regForm.$valid) {
// Call your API
console.log('Registering:', $scope.form);
}
};
});
5 - HTTP Directives: ng-get, ng-post, ng-put, ng-sse
Make HTTP requests and stream SSE from HTML using ng-get, ng-post, ng-put, ng-delete, and ng-sse — no JavaScript required for common data-fetching patterns.
AngularTS ships a family of declarative HTTP directives inspired by HTMX. Rather than wiring up $http calls in a controller, you attach ng-get, ng-post, ng-put, ng-delete, or ng-sse directly to HTML elements. The directives handle the request lifecycle, DOM insertion, loading states, error handling, and scope merging — all configured through HTML attributes.
How they differ from $http
When you use $http directly you write controller code: define the request, subscribe to the promise, update scope properties, handle errors, and trigger a digest. The HTTP directives do all of this declaratively:
<button ng-click="loadUser()">Load user</button>
<p>{{ user.name }}</p>
$http.get('/api/user/1').then(function(res) {
$scope.user = res.data;
});
};
<button ng-get="/api/user/1">Load user</button>
<p>{{ name }}</p>
When the server returns JSON, the directive merges it into scope automatically. When it returns HTML, the result is compiled and injected into the DOM using the configured swap strategy.
ng-get
ng-get fires a GET request when the configured event occurs (default: click for buttons, change for inputs, submit for forms).
<button ng-get="/api/weather?city=London">
Get weather
</button>
{{ temperature }}°C — {{ description }}
<button ng-get="/partials/user-card.html"
swap="outerHTML"
target="#user-area">
Show profile
</button>
<div id="user-area"></div>
Automatic trigger
Use trigger="load" to fire the request immediately when the element is linked (no user interaction required):
<div ng-get="/api/dashboard/stats" trigger="load">
<span ng-bind="stats.users"></span> users
</div>
Polling with interval
<div ng-get="/api/live-count"
trigger="load"
interval="5000">
{{ count }} online
</div>
ng-post
ng-post fires a POST request. On <form> elements, the default trigger is submit and the form data is collected via FormData. On buttons and other elements, the trigger defaults to click.
<form ng-post="/api/contact" name="contactForm">
<input name="email" type="email" ng-model="contact.email" required />
<textarea name="message" ng-model="contact.message" required></textarea>
<button type="submit" ng-disabled="contactForm.$invalid">Send</button>
</form>
<button ng-post="/api/cart/add"
success="cartMessage = $res.message"
error="cartError = $res.error">
Add to cart
</button>
When the form element has an enctype attribute, the request body is URL-encoded using Content-Type: application/x-www-form-urlencoded. Without enctype, form data is sent as a JSON object.
ng-put
ng-put fires a PUT request. Use it to update resources:
name="profileForm"
success="profileSaved = true"
state-success="dashboard">
<input name="name" ng-model="user.name" required />
<input name="email" type="email" ng-model="user.email" required />
<button type="submit">Save changes</button>
</form>
ng-delete
ng-delete fires a DELETE request. It shares all the same modifier attributes as ng-get:
<li>
<span>{{ item.name }}</span>
<button ng-delete="/api/items/{{ item.id }}"
swap="delete"
target="#item-{{ item.id }}">
Delete
</button>
</li>
ng-sse
ng-sse opens a persistent Server-Sent Events connection using the $sse service. Incoming messages are handled the same way as HTTP responses: JSON payloads are merged into scope, HTML strings are injected using the configured swap strategy.
<div ng-sse="/api/events/notifications"
swap="beforeend">
</div>
<div ng-sse="/api/events/market"
trigger="load">
<p>{{ price | currency }}</p>
<p>{{ change }}%</p>
</div>
The connection is torn down automatically when the scope is destroyed (e.g., when navigating away). Reconnect behaviour and error logging are provided by the $sse service.
app.get('/api/events/market', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
const interval = setInterval(() => {
res.write(`data: ${JSON.stringify({ price: 42.50, change: 0.3 })}\n\n`);
}, 2000);
req.on('close', () => clearInterval(interval));
});
Shared modifier attributes
All HTTP directives accept the same set of modifier attributes to control their behaviour:
swap
- Type:
string - Default:
innerHTML
Controls how the response HTML is inserted. Possible values:
innerHTML — replaces the element’s inner content (default)outerHTML — replaces the entire elementtextContent — inserts plain text without HTML parsingbeforebegin — inserts immediately before the elementafterbegin — inserts inside the element, before its first childbeforeend — inserts inside the element, after its last childafterend — inserts immediately after the elementdelete — removes the target element entirelynone — performs no DOM insertion (useful when only scope merging is needed)
target
A querySelector selector for the element that receives the response. When omitted, the directive element itself is the target.
<button ng-get="/api/user" target="#profile-card">Load</button>
<div id="profile-card"></div>
trigger
The DOM event that fires the request. Defaults to click for buttons and generic elements, change for inputs/selects/textareas, and submit for forms. Use "load" to trigger immediately on link.
latch
- Type:
interpolated expression
Re-fires the request every time the interpolated value changes.
<div ng-get="/api/search" latch="{{ query }}" swap="innerHTML">
{{ results.length }} results
</div>
interval
Fires the request immediately and then repeats every N milliseconds. The interval is cleared when the scope is destroyed.
<div ng-get="/api/status" interval="10000">{{ status }}</div>
delay
Wait N milliseconds before sending the request after the trigger event fires.
throttle
Ignore subsequent trigger events for N milliseconds after the request fires.
loading
When present, sets data-loading="true" on the element while the request is in flight and data-loading="false" when it completes. Useful as a CSS hook.
<button ng-get="/api/data" loading>Load</button>
button[data-loading="true"] {
opacity: 0.6;
cursor: wait;
}
loading-class
A CSS class toggled on the element while the request is in flight.
<button ng-get="/api/data" loading-class="is-loading">Load</button>
success
Expression evaluated when the response has a 2xx status code. The response data is available as $res.
<button ng-get="/api/items" success="items = $res">Fetch</button>
error
Expression evaluated when the response has a 4xx or 5xx status code. The response data is available as $res.
<button ng-post="/api/login"
success="redirect('/dashboard')"
error="loginError = $res.message">
Log in
</button>
state-success
Router state name to navigate to on success (calls $state.go).
state-error
Router state name to navigate to on error (calls $state.go).
animate
When present, enables $animate transitions for the swap operation. Requires the $animate CSS hooks to be defined.
enctype
Sets the Content-Type request header and URL-encodes form data. Use "application/x-www-form-urlencoded" to replicate a native HTML form submission.
Practical examples
Loading a data table on mount
<thead>
<tr>
<th>Name</th>
<th>Status</th>
<th>Joined</th>
</tr>
</thead>
<tbody ng-get="/api/users"
trigger="load"
swap="innerHTML">
<!-- Populated with server-rendered rows -->
</tbody>
</table>
<li ng-repeat="post in posts">{{ post.title }}</li>
</ul>
<button ng-get="/api/posts?page={{ nextPage }}"
ng-viewport
on-enter="loadMore()"
swap="beforeend"
target="#post-list">
Load more
</button>
Real-time notifications via SSE
<span ng-bind="notifications.unread"></span>
</div>
<!-- SSE merges JSON payloads into scope automatically -->
<div ng-sse="/api/events/notifications" trigger="load">
</div>
ng-post="/api/auth/login"
state-success="app.dashboard"
error="authError = $res.message">
<input type="email" name="email" ng-model="credentials.email" required />
<input type="password" name="password" ng-model="credentials.password" required />
<p ng-if="authError" ng-bind="authError"></p>
<button type="submit" ng-disabled="loginForm.$invalid">Log in</button>
</form>
6 - Directive Overview
Learn how AngularTS directives attach behavior to HTML, how built-in directive groups are organized, and how to create custom directives.
Directives are how AngularTS attaches behavior and structure to HTML. A
directive is a marker on a DOM element or attribute that tells $compile to
attach behavior, transform DOM, wire events, create scopes, or connect
controllers.
Every built-in directive in AngularTS is applied as an HTML attribute using the
ng- prefix. The compiler normalizes prefixes, so ng-bind,
data-ng-bind, and x-ng-bind all match the same directive.
Exact custom directive contracts live in TypeDoc:
How Directives Are Matched
The restrict option controls where a directive can appear.
| Restrict | Match form | Example |
|---|
A | Attribute | <span ng-bind="name"></span> |
E | Element | <my-widget></my-widget> |
AE / EA | Attribute or element | <div ng-form> or <ng-form> |
Attribute directives are the common case in AngularTS. They keep the host
element in place and augment it.
Built-In Directive Groups
AngularTS groups its built-in directives by the user-facing job they perform.
Keep these groups in mind when choosing where to look in the docs.
| Group | Purpose | Examples |
|---|
| Data binding | Synchronize scope data with the view | ng-bind, ng-model, ng-class, ng-style |
| Structural | Add, remove, repeat, or switch DOM | ng-if, ng-repeat, ng-show, ng-hide, ng-switch |
| Forms | Track validity, dirty state, messages, and model options | form, ng-model, ng-messages, validators |
| HTTP | Trigger declarative network work from the DOM | ng-get, ng-post, ng-put, ng-delete, ng-sse |
| Animations | Coordinate directive-driven animation hooks | ng-animate-swap, ng-animate-children |
| Advanced | Bridge browser APIs and integration boundaries | ng-worker, ng-wasm, ng-viewport, ng-pointer-capture |
Create A Custom Directive
Register directives on a module with .directive(name, factory). The factory
returns a directive definition object.
angular.module("demo", []).directive("highlight", () => {
return {
restrict: "A",
scope: {
color: "@highlight",
},
link(scope, element) {
const paint = (color) => {
element.style.backgroundColor = color || "yellow";
};
paint(scope.color);
scope.$watch("color", paint);
},
};
});
<p highlight="gold">Pinned note</p>
Use compile() when the directive must transform template DOM before linking.
Use link() when it only needs to attach behavior to each compiled instance.
Execution Order
When multiple directives appear on the same element, AngularTS sorts them by
priority from highest to lowest. Directives with equal priority run in
registration order.
<li ng-repeat="item in items" ng-class="{ active: item.selected }">
{{ item.name }}
</li>
Structural directives such as ng-repeat and ng-if use high priorities and
terminal behavior because they replace or remove DOM before lower-priority
directives are linked.
Scope Choice
Use the default inherited scope for simple behavior. Create a child scope when
the directive needs local state. Use isolate scope bindings when the directive is
designed as a reusable widget with explicit inputs and callbacks.
scope: {
title: "@",
value: "=",
onSave: "&",
}
Prefer components for larger reusable UI pieces. Use directives for behaviors,
DOM integrations, structural transforms, and lightweight element coordination.
Next Steps
7 - Structural Directives: ng-if, ng-repeat, ng-switch
Master AngularTS structural directives including ng-if, ng-show, ng-hide, ng-repeat with track by, ng-switch, ng-include, and ng-non-bindable.
Structural directives reshape the DOM by adding, removing, or repeating elements. Unlike attribute directives that modify an existing element, structural directives use transclusion to stamp out or remove entire sections of the template. They run at high priority (600–1000) and set terminal: true so that lower-priority directives do not compile until the structure is resolved.
ng-if
ng-if conditionally includes or removes a DOM element based on a scope expression. When the expression becomes falsy, the element and its entire subtree — including child scopes and event listeners — are destroyed. When it becomes truthy again, a fresh element and child scope are created.
<div ng-if="user.role === 'admin'">
<h2>Admin Panel</h2>
<p>Welcome, {{ user.name }}. You have full access.</p>
</div>
<div ng-if="isLoggedIn">
<p>Welcome back, {{ user.name }}!</p>
<button ng-click="logout()">Log out</button>
</div>
<div ng-if="!isLoggedIn">
<p>Please <a href="/login">sign in</a> to continue.</p>
</div>
The implementation uses transclude: "element" at priority 600, which means the compiler replaces the element with a comment placeholder and hands the element template to the transclusion function. When the expression turns truthy, the transclusion function clones the template and inserts it after the comment. When it turns falsy, the child scope is destroyed and the clone is removed.
If the $animate service is available on the element, ng-if delegates to $animate.enter and $animate.leave to trigger CSS transition hooks.
Note: ng-if vs ng-show: ng-if completely removes the element from the DOM (destroying the child scope), while ng-show keeps the element in the DOM and toggles an ng-hide CSS class. Use ng-if when the hidden content is expensive to keep around or when it should not run watchers while invisible.
Parameters
ng-if
- Type:
expression - Required: yes
An expression evaluated on each digest. When truthy, the element is inserted into the DOM in a new child scope. When falsy, the element and its child scope are destroyed.
ng-show / ng-hide
ng-show and ng-hide toggle element visibility without removing the element from the DOM. They work by adding or removing the ng-hide CSS class, which sets display: none.
ng-show
Removes ng-hide when the expression is truthy, showing the element.
<div ng-show="form.submitted">
<p>Thank you! Your form was submitted successfully.</p>
</div>
ng-hide
Adds ng-hide when the expression is truthy, hiding the element.
<div ng-hide="isLoading">
<p>Content is ready.</p>
</div>
<div ng-show="isLoading">
<p>Loading...</p>
</div>
Both directives integrate with $animate. When the animate service detects the element has animation hooks, they call $animate.addClass or $animate.removeClass with the temporary class ng-hide-animate for the duration of the animation.
.my-panel.ng-hide {
opacity: 0;
}
.my-panel.ng-hide-animate {
transition: opacity 0.3s ease;
}
ng-show
- Type:
expression - Required: yes
When truthy, ng-hide class is removed from the element, making it visible.
ng-hide
- Type:
expression - Required: yes
When truthy, ng-hide class is added to the element, hiding it.
ng-repeat
ng-repeat iterates over a collection (array or object) and stamps a copy of the template for each item. Each copy gets its own child scope populated with the iteration variables below.
Special scope properties
| Variable | Type | Description |
|---|
$index | number | Zero-based index of the current iteration |
$first | boolean | true for the first item |
$last | boolean | true for the last item |
$middle | boolean | true when neither first nor last |
$even | boolean | true when $index is even |
$odd | boolean | true when $index is odd |
Repeating over an array
<li ng-repeat="product in products">
<strong>{{ product.name }}</strong> — ${{ product.price }}
<span ng-if="$first">(newest)</span>
<span ng-if="$last">(oldest)</span>
</li>
</ul>
Repeating over an object
Use the (key, value) tuple syntax to iterate over an object’s own enumerable properties. Keys starting with $ are excluded automatically.
<dt ng-repeat="(key, value) in user">{{ key }}</dt>
<dd>{{ value }}</dd>
</dl>
Track by
By default, ng-repeat tracks items by identity (hashKey). When the collection changes, AngularTS tries to reuse existing DOM nodes by matching on the track key. The track by clause lets you provide a stable identity for items, which dramatically improves performance when working with large lists or when items are replaced by new objects from the server.
<div ng-repeat="user in users">{{ user.name }}</div>
<!-- With track by id — DOM nodes for unchanged users are reused -->
<div ng-repeat="user in users track by user.id">{{ user.name }}</div>
<!-- Track by $index — useful for arrays of primitives -->
<div ng-repeat="tag in tags track by $index">{{ tag }}</div>
Alias with as
The as clause aliases the filtered collection to a new scope variable. This is useful when you apply filters to the collection and need the filtered count.
<p>{{ filtered.length }} of {{ products.length }} items</p>
<ul>
<li ng-repeat="product in products | filter:searchText as filtered">
{{ product.name }}
</li>
</ul>
Animation support
Add the animate attribute to enable $animate.enter / $animate.leave on items:
opacity: 0;
transition: opacity 0.3s;
}
.list-item.ng-enter-active {
opacity: 1;
}
.list-item.ng-leave {
opacity: 1;
transition: opacity 0.3s;
}
.list-item.ng-leave-active {
opacity: 0;
}
Warning: Duplicate track keys in the same repeater throw an ngRepeat:dupes error. Use track by $index or a unique property like track by item.id to guarantee uniqueness.
ng-repeat
- Type:
string - Required: yes
Expression in one of the forms:
item in collection(key, value) in objectitem in collection track by expressionitem in collection | filter as alias
ng-switch
ng-switch renders exactly one child block matching the current value of a watched expression. It is the structural equivalent of a JavaScript switch statement.
<div ng-switch-when="active">
<p>Account is active. Welcome, {{ user.name }}!</p>
</div>
<div ng-switch-when="suspended">
<p>Your account has been suspended. Please contact support.</p>
</div>
<div ng-switch-when="pending">
<p>Your account is awaiting verification.</p>
</div>
<div ng-switch-default>
<p>Unknown account status.</p>
</div>
</div>
ng-switch-when registers its transclusion function under the key !value (to avoid collisions with $-prefixed keys) using an internal NgSwitchController. When the watched expression changes, the currently shown block has its scope destroyed (with an optional $animate.leave) and the matching block is transcluded into place.
Multiple values on one case
The ng-switch-when-separator attribute allows a single ng-switch-when to match multiple values:
<div ng-switch-when="Saturday Sunday" ng-switch-when-separator=" ">
Weekend!
</div>
<div ng-switch-default>Weekday</div>
</div>
ng-switch
- Type:
expression - Required: yes
The expression whose value is compared against ng-switch-when values.
ng-switch-when
- Type:
string - Required: yes
The literal string value to match against the ng-switch expression.
ng-switch-default
Rendered when no ng-switch-when matches the current value.
ng-include
ng-include fetches an external HTML template, compiles it in the current scope (or a new child scope), and inserts it into the DOM. It watches the URL expression and replaces the content whenever the URL changes.
<div ng-include="'/partials/user-profile.html'"></div>
<!-- Include a dynamic template based on user role -->
<div ng-include="'/partials/nav-' + user.role + '.html'"></div>
The directive emits three events on the scope:
$includeContentRequested — when the template request begins$includeContentLoaded — when the template is compiled and inserted$includeContentError — when the template request fails
<div ng-include="templateUrl"
ng-init="templateUrl = '/partials/home.html'"
onload="onTemplateLoaded()">
</div>
console.log('Template inserted successfully');
};
ng-include
- Type:
expression - Required: yes
Expression evaluating to the URL of the template to load. The URL is fetched via $templateRequest (which uses the template cache if available).
onload
Expression evaluated after the template is compiled and inserted.
When truthy, calls $anchorScroll after the template is inserted.
ng-non-bindable
ng-non-bindable tells the AngularTS compiler to skip the element and all its descendants entirely. No interpolation, directives, or watches are set up inside the marked section.
<pre ng-non-bindable>
<p>Use {{ expression }} to interpolate values.</p>
<div ng-bind="myValue"></div>
</pre>
<div ng-non-bindable>
{% raw %}{{ jinja_template_variable }}{% endraw %}
</div>
Note: ng-non-bindable is essential when displaying code documentation, Jinja/Twig snippets, or any content that uses {{ }} but should not be treated as AngularTS expressions.