// registration.mannequin.ts
export interface RegistrationData {
e-mail: string;
password: string;
confirmPassword: string;
acceptedTerms: boolean;
}
This interface mirrors what would usually be despatched to a back-end API. There isn’t a duplication of state, no separate “type worth” object, and no mapping required at submission time.
Creating the signal-backed type
The shape itself is created within the part utilizing a writable sign because the supply of reality. The type() operate attaches type semantics validation, discipline state, and submission to that sign.
// registration.part.ts
import { CommonModule } from "@angular/widespread";
import { Part, sign } from "@angular/core";
import {
e-mail,
type,
FormField,
required,
submit,
} from "@angular/kinds/indicators";
import { RegistrationData } from "./registration.mannequin";
@Part({
selector: "app-registration",
imports: [FormField, CommonModule],
templateUrl: "./registration.html",
styleUrl: "./registration.css",
})
export class Registration {
readonly mannequin = sign({
e-mail: "",
password: "",
confirmPassword: "",
acceptedTerms: false,
});
readonly registrationForm = type(this.mannequin, (schema) => {
required(schema.e-mail, { message: "Electronic mail is required" });
e-mail(schema.e-mail, { message: "Enter a sound e-mail handle" });
required(schema.password, { message: "Password is required" });
required(schema.confirmPassword, {
message: "Please affirm your password",
});
required(schema.acceptedTerms, {
message: "You should settle for the phrases to proceed",
});
});
async onSubmit(occasion?: Occasion) {
occasion?.preventDefault();
await submit(this.registrationForm, (worth) => {
console.log(worth());
// Mock Server Name
return Promise.resolve([
{
kind: "EmailAlreadyExists",
field: this.registrationForm.email,
error: { kind: "server", message: "Email already taken" },
},
]);
});
}
}
A number of design choices are price noting.









