@sync0/angular
v0.1.2
Published
Angular adapter for @sync0/core — field() returns a real Angular Signal
Downloads
444
Maintainers
Readme
@sync0/angular
Angular adapter for @sync0/core.
field() returns a real Angular WritableSignal<T> — no wrappers, no glue code. Drop it anywhere you'd use signal() and it just works in templates, computed(), and effect().
Installation
npm install @sync0/angular @sync0/coreRequires Angular ≥ 17.
Quick start
import { Component, computed } from '@angular/core';
import { urlField, storageField, serverField, onConflict } from '@sync0/angular';
import type { ServerConflict } from '@sync0/angular';
@Component({
standalone: true,
template: `
<input [ngModel]="query()" (ngModelChange)="query.set($event)" />
<p>Theme: {{ theme() }}</p>
@if (conflict()) {
<div class="banner">
Updated elsewhere — <button (click)="conflict.set(null)">Dismiss</button>
</div>
}
<textarea [ngModel]="notes()" (ngModelChange)="notes.set($event)"></textarea>
`,
})
export class MyComponent {
// URL search param — survives hard refresh, back/forward
readonly query = urlField('', 'q');
// localStorage — persists across page closes, syncs across tabs
readonly theme = storageField('light', 'theme');
// Server — optimistic write + realtime push
readonly notes = serverField('', 'notes');
// Conflict state — surfaces "updated elsewhere" from the server engine
readonly conflict = signal<ServerConflict<string> | null>(null);
constructor() {
onConflict(this.notes, (c) => this.conflict.set(c));
}
// computed() works on top of any field
readonly upperQuery = computed(() => this.query().toUpperCase());
}All strategies
import {
field, // full strategy object
localField, // in-memory (same as signal())
urlField, // URL search param
storageField, // localStorage
sessionField, // sessionStorage
cookieField, // document.cookie
serverField, // server adapter (Supabase, etc.)
} from '@sync0/angular';Every wrapper returns a SyncSignal<T> — a WritableSignal<T> with __raw attached for engine access.
Server adapter setup
Call configureServerAdapter() once before any server fields are created — typically in main.ts or an APP_INITIALIZER:
import { configureServerAdapter } from '@sync0/angular';
import { SupabaseAdapter } from './supabase-adapter';
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
configureServerAdapter(new SupabaseAdapter(supabase));SSR
import { registerField, serialize, hydrate } from '@sync0/angular';
// Server — after Angular SSR render pass:
registerField('query', query);
const snapshot = serialize();
// Embed snapshot in Angular TransferState
// Client — in APP_INITIALIZER, before bootstrap:
hydrate(snapshot); // fields start with server-rendered valuesCookie options
const locale = cookieField('en', 'locale', {
maxAge: 60 * 60 * 24 * 365,
sameSite: 'Lax',
});Links
- Full documentation & examples
@sync0/core— framework-agnostic engine@sync0/react— React hooks adapter@sync0/vue— Vue 3 Ref adapter
