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:
- *.wc.avt (Web Component): This file contains the logic, style, and structure of the component.
- *.data.avt (Data): This file defines the data structure used by the application.
- *.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 = "";
}
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;
}
}
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>
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>
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)