Data validation in User Interface is crucial for every application and Web Atoms makes it very easy to write.

Simple Validation

Validation accessor is decorated with @Validate decorator and it is prefixed with the word error. You can bind these accessor in UI to display errors.

For example,

```typescript

export default SignupViewModel extends AtomViewModel {

@Inject
public navigationService: NavigationService;

public model = {
    firstName: null,
    lastName: null
};

// both validate properties will return undefined value
// unless `this.isValid` is referenced.

@Validate
public get errorFirstName(): string {
    return this.model.firstName ? "" : "First name is required";
}

@Validate
public get errorLastName(): string {
    return this.model.firstName ? "" : "Last name is required";
}

public signup(): Promise<void> {

    // as soon as this property is called first time
    // validation decorator will update and error will be displayed
    if (!this.isValid) {
        await this.navigationService.alert(`Please enter required fields`);
        return;
    }

    // optional, if you want to reuse same form
    // you can call resetValidations to remove all errors
    this.resetValidations();
}

}

TSX for Webtypescript class Component extends AtomControl {

public viewModel: SignupViewModel;

public create() { this.viewModel = this.resolve(SignupViewModel); this.render(

this.viewModel.model.firstName)}/> this.viewModel.errorFirstName}/> this.viewModel.model.lastName)}/> this.viewModel.errorLastName}/> ...
   );

}

} ```

TSX for Xaml ```typescript class Component extends AtomControl {

public viewModel: SignupViewModel;

public create() { this.viewModel = this.resolve(SignupViewModel); this.render( this.viewModel.model.firstName)}/> this.viewModel.errorFirstName}/>

<XF.Entry 
    placeholder="Last name:"
    text={Bind.twoWays(() => this.viewModel.model.lastName)}/>
<XF.Label
    class="error"
    text={Bind.oneWay(()) => this.viewModel.errorLastName}/>

...

<XF.Button
    command={ () => this.viewModel.signup() }
    text="Signup"/>

); }

}

```

In above example, when page is loaded, error spans will not display anything. Even if firstName and lastName both are empty. As soon as user clicks Signup button, this.isValid get method will start watching for changes in all @Validate decorator methods and user interface will start displaying error message.

Multi View Model Validation

Larger UI will need multiple smaller UI Components, in Web Atoms, you can easily create a UI with View Model that references parent view model, parent view model's validation extends to children and it return false for isValid even if children view models are not valid.

Root Insurance View

```typescript interface IInsurance { id?: number; date?: Date; broker: string; type: string; applicants: IApplicant[]; }

export interface IApplicant { name: string; type: string; address?: string; city?: string; }

export default class InsuranceViewModel extends AtomViewModel {

@Inject
public navigationService: NavigationService;

public model: IInsurance = {
    broker: "",
    type: "General",
    applicants: [
        {
            name: "",
            type: "Primary"
        }
    ]
};

@Validate
public get errorBroker(): string {
    return this.model.broker ? "" : "Broker cannot be empty";
}

public addApplicant(): void {
    this.model.applicants.add({
        name: "",
        type: "Dependent"
    });
}

public async save(): Promise<void> {
    if (!this.isValid) {
        await this.navigationService.alert("Please fix all errors", "Error");
        return;
    }
    await this.navigationService.alert("Save Successful", "Success");
}

} ```

Insurance.html

We are displaying list of applicants in Insurance form, and we can add more applicants, note, each applicant's validation will be different based on type of applicant.

```typescript export default class Insurance extends AtomControl {

public create(): void { this.viewModel = this.resolve(InsuranceViewModel) ;

  this.render(
  <div>
     <div>
        <input
           placeholder="Name"
           value={Bind.twoWays((x) => x.viewModel.model.broker)}>
        </input>
        <span
           style="color: red"
           text={Bind.oneWay((x) => x.viewModel.errorBroker)}>
        </span>
     </div>
     <AtomItemsControl
        items={Bind.oneTime((x) => x.viewModel.model.applicants)}>
        <AtomItemsControl.itemTemplate>
           <Applicant>
           </Applicant>
        </AtomItemsControl.itemTemplate>
     </AtomItemsControl>
     <button
        eventClick={Bind.event((x) => (x.viewModel).addApplicant())}>
        Add Applicant
     </button>
     <div>Other fields...</div>
     <button
        eventClick={Bind.event((x) => (x.viewModel).save())}>
        Save
     </button>
  </div>
  );

} } ```

Nested Applicant View

Typescript ```typescript export default class ApplicantViewModel extends AtomViewModel {

@Inject
public navigationService: NavigationService;

public model: IApplicant;

@Validate
public get errorName(): string {
    return this.model.name ? "" : "Name cannot be empty";
}

@Validate
public get errorAddress(): string {
    return this.model.address ? "" : "Address cannot be empty";
}

public async delete(): Promise<void> {
    if (!( await this.navigationService.confirm("Are you sure you want to delete this?") )) {
        return;
    }
    (this.parent as InsuranceViewModel).model.applicants.remove(this.model);
}

} ```

Applicant.html

Applicant view is an independent view with its own view model, and it can also be used without parent list.

```typescript export default class Applicant extends AtomControl {

public create(): void {

   /** Following method will initialize and bind parent property of
    * ApplicantViewModel to InsuranceViewModel, this is specified in the form
    * of lambda so it will bind correctly after the control has been created
    * successfully.
    *
    * After parent is attached, parent view model will include all children validations
    * and will fail to validate if any of child is invalid
    */
  this.viewModel =  this.resolve(ApplicantViewModel, () => ({ model: this.data, parent: this.parent.viewModel })) ;

  this.render(
  <div
     style="margin: 5px; padding: 5px; border: solid 1px lightgray; border-radius: 5px">
     <div>
        <input
           placeholder="Name"
           value={Bind.twoWays((x) => x.viewModel.model.name)}>
        </input>
        <span
           style="color: red"
           text={Bind.oneWay((x) => x.viewModel.errorName)}>
        </span>
     </div>
     <div>
        <input
           placeholder="Address"
           value={Bind.twoWays((x) => x.viewModel.model.address)}>
        </input>
        <span
           style="color: red"
           text={Bind.oneWay((x) => x.viewModel.errorAddress)}>
        </span>
     </div>
     <button
        eventClick={Bind.event((x) => (x.viewModel).delete())}>
        Delete
     </button>
  </div>
  );

} } ```

When a child view is created, we are assigning parent as visual parent's view model. So whenever this child view is invalid, even parent will be invalid.