DEV Community

Akash Kava for Web Atoms

Posted on

ViewModel Decorators in Web Atoms for Xamarin.Forms

Rich View Model

Web Atoms provides feature rich View Model to write common logic with simple configuration.

Watch Decorator

Watching property is super easy with Web Atoms. You can simply create a readonly accessor and mark it is @Watch.

export default class EditorViewModel extends AtomViewModel {

    public model: CustomerModel = {
        firstName: "",
        lastName: ""
    };

    @Watch
    public get fullName() {
        const firstName = this.model.firstName;
        const lastName = this.model.lastName;
        return firstName + " " + lastName;
    }
}
Enter fullscreen mode Exit fullscreen mode

Usage

    this.render(<WA.AtomForm>
        <WA.AtomField>
            <XF.Entry text={Bind.twoWays(() =>
                this.viewModel.model.firstName)}>
        </WA.AtomField>
        <WA.AtomField>
            <XF.Entry text={Bind.twoWays(() =>
                this.viewModel.model.lastName)}>
        </WA.AtomField>
        <WA.AtomField>
            <XF.Label text={Bind.oneWay(() => this.viewModel.fullName)}>
        </WA.AtomField>
    </WA.AtomForm>);
Enter fullscreen mode Exit fullscreen mode

Validate Decorator

Validate is special type of watch, which gets activate only if validation was requested.

export default class SignupViewModel extends AtomViewModel {
    public model: CustomerModel = {
        firstName: "",
        lastName: "",
        emailAddress: ""
    };

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

    @Validate
    public get errorEmailAddress() {
        const email = this.model.emailAddress;
        if (!email) {
            return "Email is required"; 
        }
        if (!emailRegEx.test(email)) {
            return "Invalid email";
        }
        return "";
    }

    @Action({ validate: true })
    public async actionSignup() {
        // only when this was invoked
        // in UI, validation occurs
    }
}
Enter fullscreen mode Exit fullscreen mode
export default class Signup extends AtomXFContentPage {

    public viewModel: SignupViewModel;

    public create() {

        this.viewModel = this.resolve(SignupViewModel);

        this.render(<WA.AtomForm>
            <WA.AtomField>
                <XF.Entry text={Bind.twoWays(() =>
                    this.viewModel.model.firstName)}>
            </WA.AtomField>
            <WA.AtomField>
                <XF.Entry text={Bind.twoWays(() =>
                    this.viewModel.model.emailAddress)}>
            </WA.AtomField>
            <WA.AtomField>
                <XF.Button
                    command={Bind.event(() => this.viewModel.actionSignup())}
                    text="Signup">
            </WA.AtomField>
        </WA.AtomForm>);
    }
}    
Enter fullscreen mode Exit fullscreen mode

Load Decorator

Load decorator makes loading of async data very easy. Let's assume that we want to load countries and then selected state. It reports exceptions and cancels previous
operation.

export default class SignupViewModel extends AtomViewModel {

    /** This gets called on initialization */
    @Load({ init: true })
    public async loadCountries() {
        this.countries = 
            await this.restService.getCountries();
        this.selectedCountry = this.countries
            .find((x) => x.value === "IN").value;
    }

    /** This gets called when `this.selectedCountry` changes */
    @Load({ watch: true /* watch */ })
    public async loadStates(ct: CancelToken) {
        const country = this.selectedCountry;
        // pass cancel token
        // to automatically cancel previous
        // incomplete request
        this.states = 
            await this.restService.getStates(country, ct);
    }

}
Enter fullscreen mode Exit fullscreen mode

Debouncing


export default class SignupViewModel extends AtomViewModel {

    @Load({
        /** watch for changes */
        watch: true,
        /** wait as user might still be typing */
        watchDelayInMS: 500
    })
    public async loadCities(ct: CancelToken) {
        const search = this.search;

        /** cancellation of previous request is tracked here */
        this.cities = await
            this.restService.searchCities(search, ct);
    }

}

Enter fullscreen mode Exit fullscreen mode

Action

@Action decorator can be applied on a method which will automatically display an alert if there was any error. And it will invoke validation before executing the method. Display an success alert after completion if configured. You can also configure confirm to request confirmation (e.g. for delete operations).

export default class SignupViewModel extends AtomViewModel {

    /** This decorator will handle validation, display error
     * and will display an alert for success
     */
    @Action({
        validate: true,
        success: "Signup successful",
        confirm: false,
    })
    public async signup() {
        ....
    }

}
Enter fullscreen mode Exit fullscreen mode
    <XF.Button
        command={Bind.event((e) =>
            this.viewModel.signup())}
        text="Signup"
        />
Enter fullscreen mode Exit fullscreen mode

Top comments (0)