@dadan-io/record-sdk-angular adds Dadan recording to an Angular app through DadanRecorderService. The service opens the built-in recording studio from your button, runs your callbacks inside Angular’s zone and cleans up when its injector is destroyed. It supports Angular 15 to 22 and has been verified on Angular 18 to 22.
Before you start, read the Record SDK overview and get a public app ID with your development origin allowlisted.
Install the package
Link to section: Install the packagenpm install @dadan-io/record-sdk-angular react@^18 react-dom@^18The studio interface is built with React, so it needs react and react-dom even though you will not write any React code. npm 7 and later install them automatically, but choose React 19 unless you pin a version. React 18 is smaller and is the version the studio is tested with. Yarn and pnpm (without auto-install-peers) do not install them for you.
If your tsconfig.json sets "skipLibCheck": false, also run npm install -D @types/react. Recent Angular CLI projects use "moduleResolution": "bundler", which is what the package needs.
Add your public app ID to your environment files:
// src/environments/environment.ts
export const environment = {
production: false,
dadanPublicAppId: "app_your_public_id",
};The public app ID is safe in frontend code. Never put your Dadan API key in an environment file that ships to the browser.
Provide the service
Link to section: Provide the serviceDadanRecorderService is a plain class, so you can provide it without decorators. Provide it on the component that uses it, and pass NgZone so SDK callbacks run inside change detection:
import { NgZone } from "@angular/core";
import { provideDadanRecorder } from "@dadan-io/record-sdk-angular";
export const recorderProviders = [provideDadanRecorder({ ngZone: NgZone })];Providing it at component level gives each component its own studio. When the component is destroyed, Angular calls the service’s ngOnDestroy, which releases the camera, the microphone and every listener.
Add a record button
Link to section: Add a record buttonimport {
AfterViewInit,
Component,
ElementRef,
NgZone,
ViewChild,
inject,
signal,
} from "@angular/core";
import {
DadanRecorderService,
provideDadanRecorder,
type DadanVideo,
} from "@dadan-io/record-sdk-angular";
import { environment } from "../environments/environment";
@Component({
selector: "app-record-button",
standalone: true,
providers: [provideDadanRecorder({ ngZone: NgZone })],
template: `
<button #recordButton type="button" [disabled]="!ready()">
Record a video
</button>
@if (video(); as v) {
<p>
Uploaded.
<a [href]="v.sharedUrl" target="_blank" rel="noopener">
Watch your recording
</a>
</p>
}
@if (failed()) {
<p role="alert">Recording failed. Please try again.</p>
}
`,
})
export class RecordButtonComponent implements AfterViewInit {
@ViewChild("recordButton") private button!: ElementRef<HTMLButtonElement>;
private readonly recorder = inject(DadanRecorderService);
private readonly zone = inject(NgZone);
readonly ready = signal(false);
readonly failed = signal(false);
readonly video = signal<DadanVideo | null>(null);
async ngAfterViewInit() {
await this.recorder.init({
mode: "custom",
publicAppId: environment.dadanPublicAppId,
videoTitle: "Support request",
folderId: "123456",
});
this.recorder
.configureButton(this.button.nativeElement, (video) => {
this.failed.set(false);
this.video.set(video);
})
.on("error", (error) => {
this.zone.run(() => this.failed.set(true));
console.error(error.code, error.message);
});
this.ready.set(true);
}
}What happens here:
init()creates the studio with your public app ID. Call it once per service instance.configureButton()makes the button open the studio. Its callback receives theDadanVideoafter the upload finishes and the studio closes. Because the service hasNgZone, the callback runs inside the zone.configureButton()returns the studio’s button object, so you can chain more listeners such aserrorandcancel. Those run outside the zone, which is why the example wraps the update inzone.run.
The example uses signals and the @if block (Angular 17 and later). On Angular 15 or 16, use plain properties and *ngIf instead.
Open the studio from code
Link to section: Open the studio from codethis.recorder.open();
this.recorder.open({ mode: "camera" });
this.recorder.close();Passing a mode skips the mode picker. Available modes are both (screen and camera), screen, camera and audio.
Other ways to provide the service
Link to section: Other ways to provide the serviceAll of these work:
import {
DadanRecorderService,
provideDadanRecorder,
} from "@dadan-io/record-sdk-angular";
export const optionA = [DadanRecorderService];
export const optionB = [
{ provide: DadanRecorderService, useClass: DadanRecorderService },
];
export const optionC = [provideDadanRecorder()];Without provideDadanRecorder({ ngZone: NgZone }), attach the zone after injection with inject(DadanRecorderService).setZone(inject(NgZone)). If you create the service yourself with new DadanRecorderService(), call teardown() in your component’s ngOnDestroy.
Use the web component
Link to section: Use the web componentYou can also place <dadan-recorder> in a template. Register the element once, then allow custom elements in the component:
import { CUSTOM_ELEMENTS_SCHEMA, Component, inject } from "@angular/core";
import {
DadanRecorderService,
provideDadanRecorder,
} from "@dadan-io/record-sdk-angular";
import { environment } from "../environments/environment";
@Component({
selector: "app-answer-recorder",
standalone: true,
providers: [provideDadanRecorder()],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `
<dadan-recorder
[attr.public-app-id]="appId"
video-title="Interview answer"
(dadan-insert)="onInsert($event)"
></dadan-recorder>
`,
})
export class AnswerRecorderComponent {
readonly appId = environment.dadanPublicAppId;
constructor() {
inject(DadanRecorderService).defineElement();
}
onInsert(event: Event) {
const video = (event as CustomEvent).detail;
console.info(video.sharedUrl);
}
}The element dispatches dadan-insert, dadan-upload-complete, dadan-cancel and dadan-error events. See Customize the recording studio for all its attributes.
Custom recorder screens
Link to section: Custom recorder screensTo build every screen yourself, use DadanRecorder, which this package re-exports. Create it in ngOnInit or ngAfterViewInit, call destroy() in ngOnDestroy, and follow Build a custom recorder with DadanRecorder.
Troubleshooting
Link to section: Troubleshooting- NG0204 when injecting the service. Provide the class itself or use
provideDadanRecorder(). Do not add a constructor parameter in a subclass without decorating it. - “Call init() before configureButton()”.
configureButtonran beforeinit()resolved. Awaitinit()first, as in the example. - The view does not update after recording. Provide the service with
provideDadanRecorder({ ngZone: NgZone }), and wrap updates from other listeners inzone.run.