Implement runner using Angular
If you use Angular you can easily wrap a stock runner into an Angular component. This makes it possible to use the runner in an Angular application.
â Add packages to your projectâ
First, you need to add the required packages to your project. To do so, run the following command:
- Autoscroll
- Chat
- Classic
npm install tripetto-runner-autoscroll tripetto-runner-foundation react react-dom
npm install tripetto-runner-chat tripetto-runner-foundation react react-dom
npm install tripetto-runner-classic tripetto-runner-foundation react react-dom
The stock runners all depend on the Runner Foundation package and React. The Tripetto Runner Foundation library is the actual workhorse of the Tripetto runners. It parses the form definition and prepares it for UI rendering. React is used for the actual rendering.
đ Basic implementationâ
To use the runner in an Angular application, you need to create a wrapper component that runs the runner outside of Angular. Running outside of Angular is important, as the runner has its own change detection. By running it using the runOutsideAngular
method you can avoid unnecessary and costly change detection (a good article about this strategy can be found here). The code below shows the best practice to run the runner in an Angular application with maximum performance.
- Autoscroll
- Chat
- Classic
import { Component, Input, ElementRef, NgZone, OnInit, OnDestroy, ChangeDetectionStrategy } from "@angular/core";
import { run, IAutoscrollRunner } from "tripetto-runner-autoscroll";
import { IDefinition } from "tripetto-runner-foundation";
@Component({
selector: "tripetto-autoscroll",
template: "",
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AutoscrollRunnerComponent implements OnInit, OnDestroy {
private runner?: IAutoscrollRunner;
private initialDefinition?: IDefinition;
@Input() set definition(definition: IDefinition | undefined) {
if (this.runner) {
this.zone.runOutsideAngular(() => {
this.runner.definition = definition;
});
return;
}
this.initialDefinition = definition;
}
get definition(): IDefinition | undefined {
return (this.runner && this.runner.definition) || this.initialDefinition;
}
constructor(private element: ElementRef, private zone: NgZone) {}
ngOnInit() {
// Leave the builder outside of Angular to avoid unnecessary and costly change detection.
this.zone.runOutsideAngular(async () => {
this.runner = await run({
element: this.element.nativeElement,
definition: this.definition
});
});
}
ngOnDestroy() {
if (this.runner) {
this.runner.destroy();
this.runner = undefined;
}
}
}
import { Component, Input, ElementRef, NgZone, OnInit, OnDestroy, ChangeDetectionStrategy } from "@angular/core";
import { run, IChatRunner } from "tripetto-runner-chat";
import { IDefinition } from "tripetto-runner-foundation";
@Component({
selector: "tripetto-chat",
template: "",
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChatRunnerComponent implements OnInit, OnDestroy {
private runner?: IChatRunner;
private initialDefinition?: IDefinition;
@Input() set definition(definition: IDefinition | undefined) {
if (this.runner) {
this.zone.runOutsideAngular(() => {
this.runner.definition = definition;
});
return;
}
this.initialDefinition = definition;
}
get definition(): IDefinition | undefined {
return (this.runner && this.runner.definition) || this.initialDefinition;
}
constructor(private element: ElementRef, private zone: NgZone) {}
ngOnInit() {
// Leave the builder outside of Angular to avoid unnecessary and costly change detection.
this.zone.runOutsideAngular(async () => {
this.runner = await run({
element: this.element.nativeElement,
definition: this.definition
});
});
}
ngOnDestroy() {
if (this.runner) {
this.runner.destroy();
this.runner = undefined;
}
}
}
import { Component, Input, ElementRef, NgZone, OnInit, OnDestroy, ChangeDetectionStrategy } from "@angular/core";
import { run, IClassicRunner } from "tripetto-runner-classic";
import { IDefinition } from "tripetto-runner-foundation";
@Component({
selector: "tripetto-classic",
template: "",
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ClassicRunnerComponent implements OnInit, OnDestroy {
private runner?: IClassicRunner;
private initialDefinition?: IDefinition;
@Input() set definition(definition: IDefinition | undefined) {
if (this.runner) {
this.zone.runOutsideAngular(() => {
this.runner.definition = definition;
});
return;
}
this.initialDefinition = definition;
}
get definition(): IDefinition | undefined {
return (this.runner && this.runner.definition) || this.initialDefinition;
}
constructor(private element: ElementRef, private zone: NgZone) {}
ngOnInit() {
// Leave the builder outside of Angular to avoid unnecessary and costly change detection.
this.zone.runOutsideAngular(async () => {
this.runner = await run({
element: this.element.nativeElement,
definition: this.definition
});
});
}
ngOnDestroy() {
if (this.runner) {
this.runner.destroy();
this.runner = undefined;
}
}
}
đĨ Collecting response dataâ
The next step is actual data retrieval from the form. To do so, you use the onSubmit
event. This event fires when the form completes and the response data is ready for further processing. The event receives a reference to the active form instance. Together with one of the Export
functions from the Runner Foundation package, you use it to retrieve data in a convenient format. The following example shows how to export the data using the exportables
or CSV
function.
- Autoscroll
- Chat
- Classic
import { Component, Input, ElementRef, NgZone, OnInit, OnDestroy, ChangeDetectionStrategy } from "@angular/core";
import { run, IAutoscrollRunner } from "tripetto-runner-autoscroll";
import { Export, IDefinition } from "tripetto-runner-foundation";
@Component({
selector: "tripetto-autoscroll",
template: "",
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AutoscrollRunnerComponent implements OnInit, OnDestroy {
private runner?: IAutoscrollRunner;
private initialDefinition?: IDefinition;
@Input() set definition(definition: IDefinition | undefined) {
if (this.runner) {
this.zone.runOutsideAngular(() => {
this.runner.definition = definition;
});
return;
}
this.initialDefinition = definition;
}
get definition(): IDefinition | undefined {
return (this.runner && this.runner.definition) || this.initialDefinition;
}
constructor(private element: ElementRef, private zone: NgZone) {}
ngOnInit() {
// Leave the builder outside of Angular to avoid unnecessary and costly change detection.
this.zone.runOutsideAngular(async () => {
this.runner = await run({
element: this.element.nativeElement,
definition: this.definition,
onSubmit: (instance) => {
// This exports all exportable data in the form
const exportables = Export.exportables(instance);
// Iterate through all the fields
exportables.fields.forEach((field) => {
// Output each field name and value to the console
console.log(`${field.name}: ${field.string}`);
});
// This exports the collected data as a CSV object
const csv = Export.CSV(instance);
// Output CSV to the console
console.log(csv.fields);
console.log(csv.record);
}
});
});
}
ngOnDestroy() {
if (this.runner) {
this.runner.destroy();
this.runner = undefined;
}
}
}
import { Component, Input, ElementRef, NgZone, OnInit, OnDestroy, ChangeDetectionStrategy } from "@angular/core";
import { run, IChatRunner } from "tripetto-runner-chat";
import { Export, IDefinition } from "tripetto-runner-foundation";
@Component({
selector: "tripetto-chat",
template: "",
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChatRunnerComponent implements OnInit, OnDestroy {
private runner?: IChatRunner;
private initialDefinition?: IDefinition;
@Input() set definition(definition: IDefinition | undefined) {
if (this.runner) {
this.zone.runOutsideAngular(() => {
this.runner.definition = definition;
});
return;
}
this.initialDefinition = definition;
}
get definition(): IDefinition | undefined {
return (this.runner && this.runner.definition) || this.initialDefinition;
}
constructor(private element: ElementRef, private zone: NgZone) {}
ngOnInit() {
// Leave the builder outside of Angular to avoid unnecessary and costly change detection.
this.zone.runOutsideAngular(async () => {
this.runner = await run({
element: this.element.nativeElement,
definition: this.definition,
onSubmit: (instance) => {
// This exports all exportable data in the form
const exportables = Export.exportables(instance);
// Iterate through all the fields
exportables.fields.forEach((field) => {
// Output each field name and value to the console
console.log(`${field.name}: ${field.string}`);
});
// This exports the collected data as a CSV object
const csv = Export.CSV(instance);
// Output CSV to the console
console.log(csv.fields);
console.log(csv.record);
}
});
});
}
ngOnDestroy() {
if (this.runner) {
this.runner.destroy();
this.runner = undefined;
}
}
}
import { Component, Input, ElementRef, NgZone, OnInit, OnDestroy, ChangeDetectionStrategy } from "@angular/core";
import { run, IClassicRunner } from "tripetto-runner-classic";
import { Export, IDefinition } from "tripetto-runner-foundation";
@Component({
selector: "tripetto-classic",
template: "",
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ClassicRunnerComponent implements OnInit, OnDestroy {
private runner?: IClassicRunner;
private initialDefinition?: IDefinition;
@Input() set definition(definition: IDefinition | undefined) {
if (this.runner) {
this.zone.runOutsideAngular(() => {
this.runner.definition = definition;
});
return;
}
this.initialDefinition = definition;
}
get definition(): IDefinition | undefined {
return (this.runner && this.runner.definition) || this.initialDefinition;
}
constructor(private element: ElementRef, private zone: NgZone) {}
ngOnInit() {
// Leave the builder outside of Angular to avoid unnecessary and costly change detection.
this.zone.runOutsideAngular(async () => {
this.runner = await run({
element: this.element.nativeElement,
definition: this.definition,
onSubmit: (instance) => {
// This exports all exportable data in the form
const exportables = Export.exportables(instance);
// Iterate through all the fields
exportables.fields.forEach((field) => {
// Output each field name and value to the console
console.log(`${field.name}: ${field.string}`);
});
// This exports the collected data as a CSV object
const csv = Export.CSV(instance);
// Output CSV to the console
console.log(csv.fields);
console.log(csv.record);
}
});
});
}
ngOnDestroy() {
if (this.runner) {
this.runner.destroy();
this.runner = undefined;
}
}
}
The onSubmit
event supports some additional features for error handling. Have a look at the Collecting response data guide for more information and guidance.
đ Referenceâ
- Autoscroll
- Chat
- Classic
Have a look at the complete autoscroll runner API reference for detailed documentation. In the examples above, the following symbols were used:
Have a look at the complete chat runner API reference for detailed documentation. In the examples above, the following symbols were used:
Have a look at the complete classic runner API reference for detailed documentation. In the examples above, the following symbols were used:
âī¸ Up nextâ
Now you've got the basic implementation for the runner up and running, dive deeper into the following topics:
- đž Collecting response data
- đ¨ Style your forms
- đ Using fonts
- đĨī¸ Display modes
- đ Prefilling forms
- đĨ Runtime data usage
- â¯ī¸ Enable pause and resume
- đ§ Form data persistency
- đ File uploads
- đ Loading translations and locale data
- â Validating response data
- đ¤ Prevent form spamming
- đ Track usage
- đšī¸ Controlling the runner
- đ¸ Disable Tripetto branding
- đĄī¸ Content Security Policy
- đĻ Custom blocks
- đ Package overview