DEV Community

Maxime Bétrisey
Maxime Bétrisey

Posted on

Exploring Generics in Aventus: Empowering Customized Forms

Introduction

In our previous article, we delved into the concept of inheritance within Aventus. Today, we continue our exploration by diving into the world of generics. Generics provide a powerful tool for creating flexible and reusable components, and they play a crucial role in enhancing the customization capabilities of Aventus. In this article, we will walk you through a practical example that demonstrates the usage of generics in creating dynamic forms. But first, let's revisit the file extensions we've encountered before.

Understanding the File Extensions

In Aventus, we utilize the following file extensions:

  1. *.wc.avt (Web Component): This file contains the logic, style, and structure of the component.
  2. *.data.avt (Data): This file defines the data structure used by the application.
  3. *.ram.avt (RAM - Runtime Accessible Memory): This file handles the management of data in the application's memory.

The Power of Generics in Object-Oriented Programming

Generics provide a means to create reusable components that can work with multiple types of data. By utilizing generics, we can design more flexible and adaptable code, reducing redundancy and increasing code efficiency. In Aventus, generics are instrumental in creating components that can handle various data types seamlessly.

Example: Building a Generic Form Component

To illustrate the practical usage of generics in Aventus, let's walk through the creation of a generic form component. This component will serve as a foundation for building customized forms for different data types.

Step 1: Defining the Data Structure

We start by creating a data class, in this case, the Person.data.avt file. The data class represents the structure of the data we want to work with. For our example, we define a simple Person class with properties such as id, firstname, and lastname.

export class Person extends Aventus.Data implements Aventus.IData {
    public id: number = 0;
    public firstname: string = "";
    public lastname: string = "";
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Implementing the RAM

Next, we create a RAM (Runtime Accessible Memory) class, Person.ram.avt, responsible for managing the data in memory. The RAM class extends the Aventus.Ram class, where T represents the type of data it handles. In this case, we use the Person class as our data type.

import { Person } from "../data/Person.data.avt";

export class PersonRAM extends Aventus.Ram<Person> implements Aventus.IRam {

    public static getInstance() {
        return Aventus.Instance.get(PersonRAM);
    }

    public override defineIndexKey(): keyof Person {
        return 'id';
    }

    protected override getTypeForData(objJson: Aventus.KeysObject<Person> | Person): new () => Person {
        return Person;
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Building the Generic Form Component

Now, we move on to creating the core component - the generic form component. The GenericForm.wc.avt file serves as the base class for all our custom form components. It is an abstract class that takes a generic type T, which must implement the Aventus.IData interface.

<script>
    export abstract class GenericForm<T extends Aventus.IData> extends Aventus.WebComponent implements Aventus.DefaultComponent {

        //#region static

        //#endregion


        //#region props
        @Property((target: GenericForm<T>) => {
            target.loadItem();
        })
        public data_id: number;
        //#endregion


        //#region variables
        protected data: T;
        protected ram: Aventus.Ram<T>;
        //#endregion


        //#region constructor
        constructor() {
            super();
            this.ram = this.defineRAM();
        }
        //#endregion


        //#region methods
        protected abstract defineRAM(): Aventus.Ram<T>;

        private async loadItem() {
            this.data = await this.ram.get(this.data_id);
        }

        protected async save() {
            if(this.data) {
                let result = await this.ram.updateWithError(this.data);
                if(!result.success) {
                    alert(result.errors.map(e => e.message).join(", "));
                }
            }
        }
        //#endregion

    }



</script>

<template>  
    <!-- let the slot to define needed input inside child -->
    <slot></slot>
    <div class="footer">
        <button @click="save">Save</button>
    </div>
</template>

<style> 
    :host {}
</style>
Enter fullscreen mode Exit fullscreen mode

Within the GenericForm component, we define methods for loading and saving data. The defineRAM method, which is abstract, allows each derived form component to define its corresponding RAM class.

Step 4: Creating the PersonForm Component

To demonstrate the usage of the generic form component, we create the PersonForm.wc.avt file. This component extends the GenericForm class and defines the specific form elements for capturing a person's firstname and lastname. It also overrides the defineRAM method to return the PersonRAM instance.


<script>
    import { PersonRAM } from "../../ram/Person.ram.avt";
    import { Person } from "../../data/Person.data.avt";
    import { GenericForm } from "../GenericForm/GenericForm.wc.avt";

    export class PersonForm extends GenericForm<Person> implements Aventus.DefaultComponent {

        @ViewElement()
        protected firstname: HTMLInputElement;
        @ViewElement()
        protected lastname: HTMLInputElement;

        protected override defineRAM(): Aventus.Ram<Person> {
            return PersonRAM.getInstance();
        }

        private updateFirstname() {
            this.data.firstname = this.firstname.value;
        }
        private updateLastname() {
            this.data.firstname = this.lastname.value;
        }
    }


</script>

<template>  
    <div>
        <label>Firstname</label>
        <input @element="firstname" @input="updateFirstname">
    </div>
    <div>
        <label>Lastname</label>
        <input @element="lastname" @input="updateLastname">
    </div>
</template>

<style> 
    :host {}
</style>
Enter fullscreen mode Exit fullscreen mode

Conclusion

Generics in Aventus provide a powerful means to create reusable and customizable components. By leveraging the flexibility and adaptability of generics, developers can build dynamic forms that cater to various data types effortlessly. Aventus emerges as a new contender in the world of web frameworks, empowering developers to create exceptional web experiences with its rich set of features.

To learn more about Aventus and its capabilities, visit the official website: https://aventusjs.com.

Top comments (0)