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.

<div ng-controller="TodoController as ctrl">
  <p>{{ ctrl.title }}</p>
</div>
  .controller('TodoController', ['$scope', function($scope) {
    $scope.title = 'My Todos';
  }]);

ng-scope

Assigns a stable name to the current scope so debugging tools and integration code can find it later. The name is required. ng-scope does not create a child or isolate scope.

<div ng-controller="UserController" ng-scope="userPanel">
  <p>Hello {{ user.name }}</p>
</div>

ng-init

Initializes scope variables inline in the template. Best suited for simple one-off values or prototyping.

<div ng-init="name = 'World'; count = 0">
  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.

<div>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.

<div>Scroll-tracked content</div>

ng-inject

Injects a named service directly into the scope, making it accessible in the template without a controller.

<div ng-inject="$http">
  <!-- $http is now available in this scope -->
</div>

Lazy loading and performance

ng-viewport

Uses the browser’s Intersection Observer API to run expressions when an element enters or leaves the viewport. Ideal for below-the-fold loading, lazy request triggers, analytics markers, and canvas/chart initialization.

<div
  ng-viewport
  on-enter="loadComments($entry)"
  on-leave="visible = false"
  data-viewport-threshold="0.5"
  data-viewport-margin="200px 0px"
>
  <div ng-repeat="comment in comments">{{ comment.text }}</div>
</div>

Use data-viewport-once when the enter expression should run only once:

<div ng-viewport data-viewport-once on-enter="loadOnce($entry)"></div>

ng-viewport

  • Type: marker attribute

Supported attributes:

AttributeDescription
on-enterExpression evaluated when the element intersects the viewport.
on-leaveExpression evaluated when the element stops intersecting.
data-viewport-thresholdIntersection threshold, such as 0.5 or 0, 0.5, 1.
data-viewport-marginRoot margin passed to IntersectionObserver, such as 200px 0px.
data-viewport-onceDisconnects the observer after the first enter callback.

on-enter and on-leave receive $entry and $entries locals from the Intersection Observer callback.

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.

<div
  ng-worker="/workers/sort.js"
  data-params="{ items: largeList }"
  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.

Use an empty ng-worker with data-handle="name" to reuse a worker registered through app.worker(name, script). Shared handles are application-owned rather than terminated with the directive scope.

data-params

  • Type: expression

Data to pass to the worker as the message payload.

on-result

  • Type: expression

Expression evaluated when the worker posts a result. $result contains the worker’s response.

Worker results are not inserted as HTML when this expression is absent.

data-request

  • Type: boolean data attribute

Use the standard correlated request protocol instead of consuming every stream message. This is the preferred mode for shared workers.

interval

  • Type: number

Re-send the params to the worker on a millisecond interval.

throttle

  • Type: number

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 WasmResource available on scope.

<div ng-wasm src="/wasm/math.wasm" as="math"></div>
<p>
  Result: {{ math.status === 'ready' ? math.exports.add(3, 4) : 'Loading...' }}
</p>

ng-wasm

  • Type: string
  • Required: yes

Path to the .wasm file to load.

as

  • Type: string

Scope property name to assign the WasmResource to. Defaults to wasm. The reserved object keys __proto__, constructor, and prototype are rejected.

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>'
  };
});
<my-panel>
  <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.

<pre ng-non-bindable>
  {{ 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:

DirectiveARIA attribute managed
ng-disabledaria-disabled
ng-checkedaria-checked
ng-readonlyaria-readonly
ng-requiredaria-required
ng-show / ng-hidearia-hidden
ng-modelaria-value*, role

The $aria service and module config let you configure which ARIA attributes are automatically managed:

angular.createModule('app', []).config({
  $aria: {
    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:

DirectiveDOM event
ng-clickclick
ng-dblclickdblclick
ng-focusfocus
ng-blurblur
ng-keydownkeydown
ng-keyupkeyup
ng-mouseentermouseenter
ng-mouseleavemouseleave
ng-mousemovemousemove
ng-mouseovermouseover
ng-mousedownmousedown
ng-mouseupmouseup
ng-submitsubmit (on forms)
ng-cutcut
ng-copycopy
ng-pastepaste
ng-loadload

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 generated event directives on that element:

<div
  ng-click="$ctrl.activate($event)"
  data-event-prevent
  data-event-capture
></div>

Supported policy attributes:

AttributeBehavior
data-event-preventCalls $event.preventDefault() first
data-event-stopCalls $event.stopPropagation() first
data-event-captureRegisters the listener with capture: true
data-event-onceRegisters the listener with once: true
data-event-passiveRegisters 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 an application directive that installs native pointer listeners for game and board interactions:

<div ng-pointer-capture pointer-drag></div>