si2030
si2030

Reputation: 4035

Aurelia Validation - Form is loaded with data from a fetch - validation evaluates full fields as empty

I have a form that I successfully load with data from a fetch. If I then click save on that form Aurelia Validation evaluates those form text fields that have data as empty and displays this as an error below the save button - picture below.

The validation works perfectly on an empty form but for a form with loaded values its as if the form is acting like it is empty.

Surely if there are characters in the text box either by typing or by loading from a fetch it should evaluate that in the context of "required" and pass?

Currently its not when its loaded from a fetch.

Code

Here is the typescript view model:

    import { HttpClient } from "aurelia-fetch-client";
    import { autoinject, inject, NewInstance, PLATFORM } from "aurelia-framework";
    import { Router, activationStrategy } from "aurelia-router";
    import {
        ValidationControllerFactory,
        ValidationController,
        ValidationRules,
        validateTrigger
    } from "aurelia-validation";
    import { BootstrapFormRenderer } from "../../../../services/bootstrapFormRenderer/bootstrapFormRenderer";
    //import  from '../../../../services/customValidationRules/customValidationRules'

    import { AuthService } from "../../../../services/auth/auth-service"

    @autoinject
    export class Client {
        controller: ValidationController;
        client = new ClientDetails;
        job: Job;
        visits = Array();
        hasClientId: boolean;
        heading: string = "New Client";
        headingIcon: string = "fa-user-plus";

        username: string;


        constructor(
            private authService: AuthService,
            private router: Router,
            private controllerFactory: ValidationControllerFactory
        ) {
            this.router = router;
            this.controller = controllerFactory.createForCurrentScope();
            this.controller.addRenderer(new BootstrapFormRenderer());
            this.controller.addObject(this)
            this.controller.addObject(this.client);

        }

        // Required to reload new instance.
        determineActivationStrategy() {
            return activationStrategy.replace; //replace the viewmodel with a new instance
            // or activationStrategy.invokeLifecycle to invoke router lifecycle methods on the existing VM
            // or activationStrategy.noChange to explicitly use the default behavior
        }

        activate(parms, routeConfig) {
            this.hasClientId = parms.id;

            if (this.hasClientId) {
                const headers = this.authService.header();

                fetch("/api/Client/edit/" + parms.id, {
                    method: "GET",
                    headers
                })
                    .then(response => response.json())
                    .then(data => {
                        this.client = data
                    })
                this.heading = "Edit Client"; // An id was submitted in the route so we change the heading as well.
                this.headingIcon = "fa-pencil-square-o";
            }
            return null;
        }

        submitClient() {
            console.log("gets  Here");
            console.log("this.controller.validate(): ", this.controller.validate());
            //this.controller.validate();
            if (this.controller.validate()) {
                console.log("Hi!");
            }
        }

        rowSelected(jobId: number) {
            let job = this.client.jobs.filter(f => f.id === jobId);
            if (job && job.length > 0) {
                var jobVisits = job.map(j => { return j.jobVisits; })[0];
                this.visits = jobVisits;
            }
        }
    }


    export class ClientDetails {

        clientId: number;
        clientNo: number;
        company: boolean;
        companyName: string;
        abn: string;
        isWarrantyCompany: boolean;
        requiresPartsPayment: boolean;
        clientFirstName: string;
        clientLastName: string;
        email: string;
        mobilePhone: string;
        phone: string;
        notes: string;

        address: AddressDetails;

        jobs: Job[];

        bankName: string;
        bankBSB: string;
        bankAccount: string;
        active: boolean;
        deActivated: string;
        activity: boolean;
        dateCreated: string;
        dateUpdated: string;

        creatorId: number;
        creatorName: string;
    }

    class Job {
        id: number;
        agentJobNo: number;
        jobNo: number;
        jobType: string;
        jobVisits: Visit[]
        numberOfVisits: number;
        status: string;
    }

    class Visit {
        jobVisitId: number;
        dateCreated: string;
        visitDate: string;
        startTime: string;
        endTime: string;
    }

    class AddressDetails {
        address1: string;
        address2: string;
        suburb: string;
        postcode: string;
        stateShortName: string;
        addressLocationId: number;
    }

    // Validation Rules.
    ValidationRules
        .ensure((a: ClientDetails) => a.companyName).required()
            .when((a: ClientDetails) => a.company === true)
            .withMessage('Company name is required if client is a company.')
        .ensure((a: ClientDetails) => a.clientLastName).required()
        .ensure((a: ClientDetails) => a.mobilePhone).required()
        .on(ClientDetails)

The fetch is in the activate function and only fetches if there is an id supplied. The fetch loads "client" with the data that is returned. This works and I have a form with all the data from the fetch displayed.

However, if I click the "Save" button even if the two fields "lastName" and "mobilePhone" have values in them the "submitClient()" function triggers and "this.controller.validate()" evaluates these fields to be empty.

Shouldnt it see that these fields have values in them? Is there something I am missing here?

Here is how the view has "lastName" - note that "& validate" is present.

                <div class="col-md-6">
                    <div class="col-md-3">
                        <div class="form-group">
                            <label class="control-label pull-right" for="lastname">Last Name:&nbsp;</label>
                        </div>
                    </div>
                    <div class="col-md-9">
                        <div class="form-group">
                            <input type="text" value.bind="client.clientLastName & validate" class="form-control" id="lastname" placeholder="Last Name...">
                        </div>
                    </div>
                </div>

enter image description here

Upvotes: 0

Views: 178

Answers (1)

Miroslav Popovic
Miroslav Popovic

Reputation: 12128

When loading existing client from the server, you are setting this.client property to be a simple JSON object. It will no longer be an instance of ClientDetails and hence validation rules won't work since they only work on ClientDetails instances.

You'll need to create a new instance of ClientDetails and populate it from the data object returned by fetch. It could be done manually by creating a constructor or a mapping method in ClientDetails that accepts data object and maps to each ClientDetails property (this.clientId = data.clientId, etc.).

As an alternative, you could have some kind of a generic mapper function that would do the mapping by property names. I'm not very familiar with TypeScript, but this SO question has a lot of solutions on how to do that.

Upvotes: 1

Related Questions