loading...
Web Atoms

ViewModel Decorators in Web Atoms for Xamarin.Forms

akashkava profile image Akash Kava ・2 min read

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;
    }
}

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>);

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
    }
}
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>);
    }
}    

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);
    }

}

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);
    }

}

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() {
        ....
    }

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

Posted on by:

Web Atoms

TSX instead of Xaml, TypeScript instead of C#, Hot reload Xamarin.Forms published App, use VS Code for Xamarin.Forms Development.... "Web Atoms" is an advanced MVVM framework to write cross platform applications in HTML5 and Xamarin.Forms.

Discussion

markdown guide