# Angular Image Editor

In Angular you mount Embed Editor from a standalone component. mountEditor, from the standalone entry, takes the element of an ElementRef and the options, draws the editor there and returns update and destroy. Mount it in ngAfterViewInit, send changes from ngOnChanges and destroy it in ngOnDestroy.

## Add It to Your Angular App

Free is on public npm, with no key. Lite and Full install from the private registry with your license key: put the two lines in your project's .npmrc, keep the key in an environment variable, then install by name.

### Free

```bash
npm install @embededitor/free
```

```typescript
import { AfterViewInit, Component, ElementRef, Input, OnDestroy, ViewChild } from '@angular/core';
import { mountEditor } from '@embededitor/free/standalone';

@Component({
  selector: 'app-photo-editor',
  standalone: true,
  template: '<div #host style="height: 600px"></div>'
})
export class PhotoEditorComponent implements AfterViewInit, OnDestroy {
  @Input({ required: true }) src!: string;

  @ViewChild('host') host!: ElementRef<HTMLElement>;

  private editor: ReturnType<typeof mountEditor> | null = null;

  ngAfterViewInit() {
    this.editor = mountEditor(this.host.nativeElement, {
      onProcess: ({ dest }) => {
        console.info('saved', dest.name);
      },
      src: this.src
    });
  }

  ngOnDestroy() {
    this.editor?.destroy();
    this.editor = null;
  }
}
```

### Lite

```ini
@embededitor:registry=https://get.embededitor.com/
//get.embededitor.com/:_authToken=${EMBED_EDITOR_KEY}
```

```bash
npm install @embededitor/lite
```

```typescript
import { AfterViewInit, Component, ElementRef, Input, OnDestroy, ViewChild } from '@angular/core';
import { mountEditor } from '@embededitor/lite/standalone';

@Component({
  selector: 'app-photo-editor',
  standalone: true,
  template: '<div #host style="height: 600px"></div>'
})
export class PhotoEditorComponent implements AfterViewInit, OnDestroy {
  @Input({ required: true }) src!: string;

  @ViewChild('host') host!: ElementRef<HTMLElement>;

  private editor: ReturnType<typeof mountEditor> | null = null;

  ngAfterViewInit() {
    this.editor = mountEditor(this.host.nativeElement, {
      onProcess: ({ dest }) => {
        console.info('saved', dest.name);
      },
      src: this.src
    });
  }

  ngOnDestroy() {
    this.editor?.destroy();
    this.editor = null;
  }
}
```

### Full

```ini
@embededitor:registry=https://get.embededitor.com/
//get.embededitor.com/:_authToken=${EMBED_EDITOR_KEY}
```

```bash
npm install @embededitor/full
```

```typescript
import { AfterViewInit, Component, ElementRef, Input, OnDestroy, ViewChild } from '@angular/core';
import { mountEditor } from '@embededitor/full/standalone';

@Component({
  selector: 'app-photo-editor',
  standalone: true,
  template: '<div #host style="height: 600px"></div>'
})
export class PhotoEditorComponent implements AfterViewInit, OnDestroy {
  @Input({ required: true }) src!: string;

  @ViewChild('host') host!: ElementRef<HTMLElement>;

  private editor: ReturnType<typeof mountEditor> | null = null;

  ngAfterViewInit() {
    this.editor = mountEditor(this.host.nativeElement, {
      onProcess: ({ dest }) => {
        console.info('saved', dest.name);
      },
      src: this.src
    });
  }

  ngOnDestroy() {
    this.editor?.destroy();
    this.editor = null;
  }
}
```

## The Editor You Just Installed

The Full edition with the embededitor.com badge, running in your browser. Photo by [Hugh Whyte](https://unsplash.com/photos/coastal-town-with-colorful-buildings-and-ocean-view-KIfVGXf6Kzc) on Unsplash.

## What Changes in Angular

- The View Comes First: @ViewChild gives the host element only once the view exists, so mountEditor runs in ngAfterViewInit, on host.nativeElement. Give the host a height: the editor fills it.
- Inputs Reach the Editor Through ngOnChanges: ngOnChanges calls editor.update(options). It runs before the view is ready too, and that first call does nothing.
- Typed, and Cleared on Destroy: The editor is a ReturnType<typeof mountEditor> | null field. ngOnDestroy calls destroy() and sets it back to null, which keeps a late update from throwing.
- The Inputs: The example declares src as @Input({ required: true }) and lang as an @Input that defaults to en-us.

## The Props You Will Use Most

- [src](https://docs.embededitor.com/api-reference/props#source): The photo, read at open. The component's required input.
- [onProcess](https://docs.embededitor.com/api-reference/props#callbacks): Runs when Save finishes, with the file and its state.
- [aspectRatio](https://docs.embededitor.com/api-reference/props#crop): Opens the crop at a width / height ratio; null leaves it free.
- [onClose](https://docs.embededitor.com/api-reference/props#callbacks): Wire it to a method of the component. The Close button shows only when you pass it.

[Every prop in the docs](https://docs.embededitor.com/api-reference/props)

## Angular Questions

### Does it work with standalone components?

Yes. The example is one: a standalone component with a @ViewChild host that mountEditor draws the editor into.

### Why does the first ngOnChanges do nothing?

Before the view is ready there is no editor yet, so the first call does nothing. ngAfterViewInit mounts the editor with the same options.

### Where do the options come from?

A private options() method builds { lang, src } from the inputs. ngAfterViewInit passes it to mountEditor and ngOnChanges passes it to update, so both send the same shape.

## Set it up for Angular in the playground.

Change the edition, the colors, the tools and the crop, and copy the code for your app.

[Open in the Playground](https://embededitor.com/playground#c=eyJlZGl0aW9uIjoiRlVMTCIsImZyYW1ld29yayI6IkFOR1VMQVIifQ)
