npm package: @servlyadmin/runtime-angular
Description: Angular wrapper for Servly runtime components
Latest version: 0.1.41

# @servlyadmin/runtime-angular

Angular wrapper for Servly runtime renderer. Render Servly components in your Angular applications with full slot support, state management, and reactive updates.

## Installation

```bash
npm install @servlyadmin/runtime-angular
# or
yarn add @servlyadmin/runtime-angular
# or
pnpm add @servlyadmin/runtime-angular
```

## Requirements

- Angular 14.0.0 or higher
- @servlyadmin/runtime-core (installed automatically)

## Setup

Import the `ServlyModule` in your Angular module:

```typescript
import { NgModule } from '@angular/core';
import { ServlyModule } from '@servlyadmin/runtime-angular';

@NgModule({
  imports: [
    ServlyModule,
    // ... other imports
  ],
})
export class AppModule {}
```

Or for standalone components:

```typescript
import { Component } from '@angular/core';
import { ServlyComponent, ServlySlotDirective } from '@servlyadmin/runtime-angular';

@Component({
  standalone: true,
  imports: [ServlyComponent, ServlySlotDirective],
  // ...
})
export class MyComponent {}
```

## Quick Start

```typescript
import { Component } from '@angular/core';

@Component({
  selector: 'app-example',
  template: `
    <servly-component
      id="my-component"
      [props]="{ title: 'Hello World' }"
      (load)="onLoad($event)"
      (error)="onError($event)"
    ></servly-component>
  `,
})
export class ExampleComponent {
  onLoad(data: any) {
    console.log('Component loaded:', data);
  }

  onError(error: Error) {
    console.error('Failed to load:', error);
  }
}
```

## API Reference

### ServlyComponent Inputs

| Input | Type | Default | Description |
|-------|------|---------|-------------|
| `id` | `string` | **required** | Component ID from the registry |
| `version` | `string` | `'latest'` | Version specifier |
| `props` | `object` | `{}` | Props to pass to the component |
| `state` | `object` | `undefined` | Additional state for bindings |
| `context` | `object` | `undefined` | Additional context for bindings |
| `eventHandlers` | `object` | `undefined` | Event handlers keyed by element ID |
| `cacheStrategy` | `'memory' \| 'localStorage' \| 'none'` | `'memory'` | Caching strategy |
| `retryConfig` | `object` | `undefined` | Retry configuration |
| `forceRefresh` | `boolean` | `false` | Force refresh from registry |
| `showSkeleton` | `boolean` | `true` | Show loading skeleton |
| `enableStateManager` | `boolean` | `false` | Enable state management |
| `initialState` | `object` | `undefined` | Initial state |

### Outputs

| Output | Type | Description |
|--------|------|-------------|
| `load` | `EventEmitter<ComponentData>` | Emitted on successful load |
| `error` | `EventEmitter<Error>` | Emitted on load failure |
| `slotsReady` | `EventEmitter<Record<string, HTMLElement>>` | Emitted when slots are ready |
| `stateChange` | `EventEmitter<StateChangeEvent>` | Emitted on state change |
| `ready` | `EventEmitter<void>` | Emitted when fully rendered |

### Slot Directive

Use the `servlySlot` directive to define slot content:

```html
<servly-component id="my-component">
  <ng-template servlySlot="header">
    <h1>Custom Header</h1>
  </ng-template>
  
  <ng-template servlySlot="footer">
    <p>Custom Footer</p>
  </ng-template>
</servly-component>
```

### Custom Loading/Error States

```html
<servly-component id="my-component">
  <!-- Custom loading state -->
  <div servlyFallback>
    <mat-spinner></mat-spinner>
    <p>Loading component...</p>
  </div>
  
  <!-- Custom error state -->
  <div servlyError>
    <mat-icon>error</mat-icon>
    <p>Failed to load component</p>
  </div>
</servly-component>
```

## Advanced Usage

### With State Management

```typescript
import { Component } from '@angular/core';
import { StateChangeEvent } from '@servlyadmin/runtime-angular';

@Component({
  selector: 'app-counter',
  template: `
    <servly-component
      id="counter-component"
      [props]="{ count: count }"
      [enableStateManager]="true"
      [initialState]="{ count: 0 }"
      (stateChange)="onStateChange($event)"
    ></servly-component>
  `,
})
export class CounterComponent {
  count = 0;

  onStateChange(event: StateChangeEvent) {
    if (event.key === 'count') {
      this.count = event.value;
    }
  }
}
```

### Using ServlyStateService

```typescript
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ServlyStateService } from '@servlyadmin/runtime-angular';

@Component({
  selector: 'app-stateful',
  template: `...`,
})
export class StatefulComponent implements OnInit, OnDestroy {
  private unsubscribe?: () => void;

  constructor(private servlyState: ServlyStateService) {}

  ngOnInit() {
    // Initialize state
    this.servlyState.initialize({ user: null });
    
    // Subscribe to changes
    this.unsubscribe = this.servlyState.subscribe((event) => {
      console.log('State changed:', event);
    });
  }

  ngOnDestroy() {
    this.unsubscribe?.();
  }

  updateUser(user: any) {
    this.servlyState.set('user', user);
  }

  getUser() {
    return this.servlyState.get('user');
  }
}
```

### With Event Handlers

```typescript
import { Component } from '@angular/core';

@Component({
  selector: 'app-form',
  template: `
    <servly-component
      id="form-component"
      [eventHandlers]="handlers"
    ></servly-component>
  `,
})
export class FormComponent {
  handlers = {
    'submit-btn': {
      onClick: (event: Event) => {
        console.log('Submit clicked!');
        this.handleSubmit();
      },
    },
    'email-input': {
      onChange: (event: Event) => {
        const input = event.target as HTMLInputElement;
        console.log('Email:', input.value);
      },
    },
  };

  handleSubmit() {
    // Handle form submission
  }
}
```

### Accessing Component Methods

```typescript
import { Component, ViewChild } from '@angular/core';
import { ServlyComponent } from '@servlyadmin/runtime-angular';

@Component({
  selector: 'app-example',
  template: `
    <servly-component #servly id="my-component"></servly-component>
    <button (click)="reload()">Reload</button>
  `,
})
export class ExampleComponent {
  @ViewChild('servly') servlyComponent!: ServlyComponent;

  reload() {
    // Access component methods
    this.servlyComponent.retry();
  }

  getSlotElement(name: string) {
    return this.servlyComponent.getSlotElement(name);
  }

  getSlotNames() {
    return this.servlyComponent.getSlotNames();
  }
}
```

### Version Specifiers

```html
<!-- Exact version -->
<servly-component id="my-component" version="1.2.3"></servly-component>

<!-- Caret range -->
<servly-component id="my-component" version="^1.0.0"></servly-component>

<!-- Tilde range -->
<servly-component id="my-component" version="~1.2.0"></servly-component>

<!-- Latest -->
<servly-component id="my-component" version="latest"></servly-component>
```

### Reactive Props

```typescript
import { Component } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

@Component({
  selector: 'app-reactive',
  template: `
    <servly-component
      id="my-component"
      [props]="props$ | async"
    ></servly-component>
  `,
})
export class ReactiveComponent {
  props$ = new BehaviorSubject({ count: 0 });

  increment() {
    const current = this.props$.value;
    this.props$.next({ count: current.count + 1 });
  }
}
```

## TypeScript Support

Full TypeScript support is included:

```typescript
import type {
  ComponentData,
  BindingContext,
  StateChangeEvent,
  CacheStrategy,
  RetryConfig,
} from '@servlyadmin/runtime-angular';
```

## Re-exported Utilities

```typescript
import {
  // Fetching
  fetchComponent,
  prefetchComponents,
  setRegistryUrl,
  
  // Caching
  invalidateCache,
  clearAllCaches,
  
  // Version utilities
  parseVersion,
  compareVersions,
  
  // State management
  StateManager,
  getValueByPath,
  
  // Event system
  EventSystem,
  getEventSystem,
} from '@servlyadmin/runtime-angular';
```

## License

MIT