AbsoluteJS

Angular Islands

Angular hosts import the standalone Island component and use the <absolute-island> element in templates. Angular islands consume shared state through the injected IslandStore service.

#Authoring

Angular templates stay declarative. Import Island into the component and use <absolute-island> where you want the island.

TS
import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
import { Island } from '@absolutejs/absolute/angular';

@Component({
  imports: [CommonModule, Island],
  selector: 'angular-host-page',
  standalone: true,
  template:                 '<absolute-island
' +
      '  component="ReactCounter"
' +
      '  framework="react"
' +
      '  hydrate="load"
' +
      '  [props]="{ initialCount: 0 }"
' +
      '/>'
})
export class AngularHostComponent {}

#Stores

Use the injected IslandStore service to read and subscribe to selectors from a shared island store. This keeps the state model store-first, not prop-key-first.

TS
import '@angular/compiler';
import {
  ChangeDetectorRef,
  Component,
  inject,
  Input,
  OnDestroy,
  OnInit,
  signal
} from '@angular/core';
import { Subscription } from 'rxjs';
import { IslandStore } from '@absolutejs/absolute/angular';
import { counterIslandStore } from '../../islands/counterStore';

class AngularCounterImpl implements OnDestroy, OnInit {
  static __absoluteProps = {
    initialCount: 0,
    label: ''
  };

  readonly changeDetectorRef = inject(ChangeDetectorRef);
  readonly islandStore = inject(IslandStore);
  readonly incrementSharedAction = this.islandStore.get(
    counterIslandStore,
    (state) => state.incrementShared
  );
  subscription = new Subscription();
  initialCount = 0;
  label = '';
  readonly count = signal(this.initialCount);
  readonly sharedCount = signal(0);

  ngOnInit() {
    this.count.set(this.initialCount);
    this.subscription.add(
      this.islandStore
        .select(counterIslandStore, (state) => state.sharedCount)
        .subscribe((value) => {
          this.sharedCount.set(Number(value));
          this.changeDetectorRef.detectChanges();
        })
    );
  }

  increment() {
    this.count.update((value) => value + 1);
  }

  incrementShared() {
    this.incrementSharedAction();
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

Component({
  selector: 'abs-angular-counter',
  standalone: true,
  template:                 '<div>
' +
      '  <p>{{ label }}</p>
' +
      '  <strong>Local: {{ count() }}</strong>
' +
      '  <strong>Shared: {{ sharedCount() }}</strong>
' +
      '  <button (click)="increment()">Increment Angular</button>
' +
      '  <button (click)="incrementShared()">Increment Shared</button>
' +
      '</div>'
})(AngularCounterImpl);
Input()(AngularCounterImpl.prototype, 'initialCount');
Input()(AngularCounterImpl.prototype, 'label');

export const AngularCounter = AngularCounterImpl;