What is the process of generating a map from a class to retrieve its attributes as values?

I am looking to establish a more robust type-safe connection between an Angular template and a FormGroup. I have an idea in mind but I'm unsure how to properly implement it in TypeScript.

My goal is to utilize an object to define the keys of the controls within a FormGroup (which is the initial parameter for the FormControl constructor), as well as bind the formControlName within the template, all based on a model class.

The rationale behind this approach is to eliminate the reliance on "magic strings" when using strings for the formControlName attribute. By doing so, the model class becomes the single source of truth, making it easier to refactor the class and automatically update all other references.

For instance, I envision being able to write something similar to the following:

// model class:
class Partner {
  id: string;
  name: string;
}

// defining static type SomeType = { id: "id", name: "name" } that extends public properties of Partner
// where the property values must match the property names.

// component:
@Component({
  template: `
    <form [formGroup]="form" (ngSubmit)="onSubmit()" novalidate>
      <!-- avoiding the use of magic strings like formControlName="id" -->
      <input type="text" [formControlName]="SomeType.id">
      <input type="text" [formControlName]="SomeType.name">
    </form>
`})
class CreatePartnerComponent {
  form = new FormGroup({
    SomeType.id: new FormControl(''),
    SomeType.name: new FormControl('')
  });
}

Thank you!

Answer №1

I may have stumbled upon a solution.

class ModelClass {
  id: string;
  name: string;
}

// To bind form control names as strings in the template, an object is created within the component class:

readonly fcName: { [key in keyof ModelClass]: key } = {
  // The downside is having to manually update this property when ModelClass is refactored
  // However, forgetting to do so will result in an error, and some IDEs can assist with auto-filling.
  id: 'id',
  name: 'name'
}

// Form controls are bound using [formControlName]="fcName.id" and so on,
// making it clear in the template.

// The form group is declared like this, enforcing correct key names based on the model:

form = new FormGroup({
  id: new FormControl(''),
  name: new FormControl('')
} as { [key in keyof ModelClass]: FormControl });

Is there a more elegant approach that avoids the need for the fcNames variable and eliminates duplicate code while still binding the correct form control names in the template?

Answer №2

Although it may seem tardy, you can achieve the task by utilizing the setValue method in a straightforward manner. Just use

this.formGroup.setValue(this.preFilledData)

Answer №3

Utilizing a method like the one shown above is an effective way to steer clear of relying on arbitrary magic string values in your code. A potential solution could look like this:

class ModelClass {
    id: string;
    age: number;
}
class MyFormGroup extends FormGroup {
    controls: {
        [key in keyof ModelClass]: AbstractControl;
    };
}
@Component({
    template: `
        <form [formGroup]="form"
              (ngSubmit)="onSubmit()"
              novalidate>
            <input type="text"
                   [formControl]="form.controls.id" />
            <input type="text"
                   [formControlName]="form.controls.age" />
        </form>
    `,
})
class CreatePartnerComponent {
    someEnum = MyFormGroup; // trick to use enum inside the template
    formConfig: { [key in keyof ModelClass]: FormControl } = {
        id: new FormControl(),
        age: new FormControl(),
    };
    form = new FormGroup(this.formConfig) as MyFormGroup;
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The router's handler function sends back a collection of objects, but for some reason, the client is not receiving them in JSON format even though the response

I am currently developing an Express.js project using Typescript. In my project, I have defined an enum and an interface as follows: export enum ProductCategory { ELECTRONICS = 'electronics', CLOTHING = 'clothing', TOYS = & ...

The Jest type definitions seem to be malfunctioning in this TypeScript project

Recently, I began a new Typescript project utilizing GTS. While the typings are functioning correctly for regular *.ts files, I am encountering difficulties in getting *.spec.ts files to work. Issue Each jest function is being flagged as red by ESLint wit ...

The system detected an Image with the source "/images/logo.png" as the primary element contributing to Largest Contentful Paint (LCP)

I have been working on a project using Next.13 and Typescript. In order to display an Image, I created a component called Logo.tsx. "use client"; import Image from "next/image"; import { useRouter } from "next/navigation"; c ...

Changing the color of the pre-selected date in the mat-datepicker Angular component is a key feature that can enhance the

Is there a way to adjust the color of the preselected "today" button in mat-datepicker? https://i.stack.imgur.com/wQ7kO.png ...

Is there a way to dynamically define the return type of a function in Typescript?

Can the variable baz be dynamically assigned the string type? type sampleType = () => ReturnType<sampleType>; // Want to return the type of any function I pass (Eg. ReturnType<typeof foo>) interface ISampleInterface { baz: sampleType; } ...

Specify a maximum length for numerical input and permit the use of a plus sign

I am currently working on a form field where users can input a country code. I want to ensure that the user enters only 3 numbers with a + sign at the beginning. I have successfully limited the length to 3 digits, but I am facing an issue with typing the ...

Difficulty in activating or deactivating form controls in Angular 2 upon receiving an HTTP response

When using formcontrol in Angular, I encountered an issue where I tried to disable the form control based on a variable assigned from an HTTP response. Angular2 gave me a warning message. The warning mentioned that it's not recommended to use the dis ...

Using Angular's HttpClientModule to set up custom request headers

I have been attempting to make an API call using the Angular HttpClientModule: getUser(ticket) { console.log(ticket); // Returns valid ticket return this.http.get('https://myapi/api/v1/flow-analysis', { headers: new Htt ...

Testing components in React involves creating or invoking specific construct or call signatures

Exploring this React component: import { Meta } from '@storybook/react'; export function MyComponentExample() { return ( <div>my component example</div> ); } export default { component: MyComponentExample, title: 'M ...

I continue encountering the Server Error message: "Error: The default export on page "/products/all" is not a React Component."

I have been trying to create a page to display all the products listed in my server.json file (shown below). { "products": [ { "slug": "live-by-the-sun-love-by-the-moon", "title": "Live by the sun, love by the moon.", "price" ...

Issue encountered while deploying Firebase Functions: Unable to parse function triggers

Experiencing difficulty deploying firebase functions from an angular project after updating to the latest firebase-tools 7.8.1 version. The project's package.json contains "firebase-admin": "~6.0.0", "firebase-functions": "^2.1.0", and "firebase-funct ...

What is preventing me from running UNIT Tests in VSCode when I have both 2 windows and 2 different projects open simultaneously?

I have taken on a new project that involves working with existing unit tests. While I recently completed a course on Angular testing, I am still struggling to make the tests run smoothly. To aid in my task, I created a project filled with basic examples f ...

Guide on how to specify the return type for useMutation in the 'react-query' library

Here is the code snippet provided: const setFriendCode = (data: Params) => api({ data }) const [mutateSetFriendCode, state] = useMutation<Response, Params>( setFriendCode ) An issue arises with the type of parameters in the code. The compiler ...

What could be causing my NextJS application to not recognize the _document.tsx file?

Seeking assistance in understanding why my _document.tsx is not loading properly within my nextJS application. My Attempts So Far I have been diligently following the NextJS documentation for creating a custom _document.js. Despite my efforts, I am unable ...

PrimeNG Default DataTable - Customize Column Visibility

My datatable currently has 11 columns, and I am utilizing the toggleable columns feature. However, I want to display only 4 selected columns initially out of the 11 options available. I have researched various possibilities using the MultiSelect component, ...

Unable to locate the module 'next' or its associated type declarations

Encountering the error message Cannot find module '' or its corresponding type declarations. when trying to import modules in a Next.js project. This issue occurs with every single import. View Preview Yarn version: 3.1.0-rc.2 Next version: 1 ...

Here's how you can combine several elements from one array and assign them to the first element of another array:

I'm working with an array that looks like this: public static readonly List: Array<any> = [ { name: 'CCS', link: 'Dummy link1' }, { name: 'CCR', link: 'Dummy link2' }, { name: 'PM', ...

Why does HttpClient in Angular 4 automatically assume that the request I am sending is in JSON format?

Currently, I am working with Angular 4's http client to communicate with a server that provides text data. To achieve this, I have implemented the following code snippet: this.http.get('assets/a.txt').map((res:Response) => res.text()).s ...

The element is implicitly given an 'any' type due to the fact that a string expression cannot be used to index the following type: { "1" : { "key": string}; "2" : { "key": string};}

I have a JSON file containing IDs as keys like this: "1" : { "key": "value"}, "2" : { "key": "value"}, In my class, I import this JSON file as a data object and then use the ID passed to a method ...

Invoke the router function dynamically

I am looking for a way to simplify route registration without manually writing out app.get('/', function (req, res, next) { }); each time. I want to automate this process by passing in a router object like the one below... { path: '&ap ...