Creating an interactive element in Angular: A step-by-step guide

Hey there, I'm facing a problem with creating a specific payload structure as shown in the example code snippet below. Everything was going well until I got stuck at the dynamic form part, and despite several attempts, I can't seem to figure it out. Any help would be greatly appreciated.

The issue lies in constructing the structure mentioned in the first block of code, particularly in the dataCollege field where I'm unable to save the dynamically changing array.

I've attempted placing FormControl in the HTML where I render the dynamic inputs, but it keeps throwing an error.

The payload structure I managed to put together involves the first three fields successfully. However, the last field, datosCole, is giving me trouble as it's dynamic based on the GradosCole field, which can have different values like initial (2 values), primary (3 values), and secondary (4 values).

{ name: Sara,
  age: 14, 
  gradeCollege: Secondary, 
  dataCollege: {         
                 course: "Maths",
                 schedule: "Afternoon",
                 section: "A",
                 teacher: "Mateo",
               }
}

This is how my form is structured in HTML.

<div>
  <form [formGroup]="form">

<!-- Name -->
<mat-form-field>
  <input matInput type="text" placeholder="Name" [formControl]="name">
</mat-form-field>

<!-- Age -->
<mat-form-field>
  <input matInput type="text" placeholder="Age" [formControl]="age">
</mat-form-field>

<!-- Grade -->
<mat-form-field>
  <mat-label>Grade</mat-label>
  <mat-select [formControl]="gradeCollege">
    <mat-option *ngFor="let item of grades" [value]="item" (click)="getData(item)">
      {{item}}
    </mat-option>
  </mat-select>
</mat-form-field>

<!-- dynamic data -->
<div *ngFor="let item of data; index as i" >
  <mat-form-field>
    <input matInput [type]="item.type" [placeholder]="item.label" [formControl]="dataCollege" >
  </mat-form-field>
</div>

 </form>

  <div>
    <button type="button" (click)="save()">Save</button>
  </div>
</div>

This is the TypeScript file related to the form.

import { Component, OnInit } from '@angular/core'; import { FormGroup, FormBuilder, Validators, AbstractControl } from '@angular/forms';

@Component({ selector: 'app-datos', templateUrl: './datos.component.html', styleUrls: ['./datos.component.scss'] }) export class DatosComponent implements OnInit {

public grades: any[] = ['initial','primary','secondary']; 
public data: any[] = []

// Form 
public form: FormGroup; 
public name: AbstractControl; 
public age: AbstractControl; 
public gradeCollege: AbstractControl; 
public dataCollege: AbstractControl;

constructor( protected fb: FormBuilder, ) {

}

ngOnInit() {
 this.form = this.fb.group({ 
             name: ['', Validators.compose([Validators.required])], 
             age: [''], 
             gradeCollege: ['', Validators.compose([Validators.required])], 
             dataCollege:  ['']
}); 

this.name= this.form.get('name');
this.age = this.form.get('age');
this.gradeCollege = this.form.get('gradeCollege');
this.dataCollege = this.form.get('dataCollege'); }

getData (typeGrade: any) {

console.log(typeGrade);

if(typeGrade === 'initial'){
  this.data = null
  this.data = [
    {type: 'text', label: 'course'},
    {type: 'text', label: 'schedule'},
  ]
}

if(typeGrade === 'primary'){
  this.data = null
  this.data = [
    {type: 'text', label: 'course'},
    {type: 'text', label: 'schedule'},
    {type: 'text', label: 'section'}
  ]
}

if(typeGrade === 'secondary'){
  this.data= null
  this.data = [
    {type: 'text', label: 'course'},
    {type: 'text', label: 'schedule'},
    {type: 'text', label: 'section'},
    {type: 'text', label: 'teacher'}
  ]
}

}

submit() {
 const payload = {
  name: this.name.value,
  age: this.age.value,
  gradeCollege: this.gradeCollege.value,
  dataCollege: this.dataCollege.value,
 };

 console.log(payload)

}}

Error Message:

ERROR TypeError: ctx.save is not a function at ActionDialogComponent_Template_button_click_13_listener (template.html:34:36) at executeListenerWithErrorHandling (core.js:21860:1) at wrapListenerIn_markDirtyAndPreventDefault (core.js:21902:1) at HTMLButtonElement. (platform-browser.js:976:1) at ZoneDelegate.invokeTask (zone-evergreen.js:399:1) at Object.onInvokeTask (core.js:41686:1) at ZoneDelegate.invokeTask (zone-evergreen.js:398:1) at Zone.runTask (zone-evergreen.js:167:1) at ZoneTask.invokeTask [as invoke] (zone-evergreen.js:480:1) at invokeTask (zone-evergreen.js:1621:1) defaultErrorLogger @ core.js:6241 handleError @ core.js:6294 handleError @ core.js:13881 executeListenerWithErrorHandling @ core.js:21863 wrapListenerIn_markDirtyAndPreventDefault @ core.js:21902 (anonymous) @ platform-browser.js:976 invokeTask @ zone-evergreen.js:399 onInvokeTask @ core.js:41686 invokeTask @ zone-evergreen.js:398 runTask @ zone-evergreen.js:167 invokeTask @ zone-evergreen.js:480 invokeTask @ zone-evergreen.js:1621 globalZoneAwareCallback @ zone-evergreen.js:1647

Answer №1

If you prefer the "dataCollege" to be an Array instead of an object, it could look something like this:

For instance:

{ name: Sara,
  age: 14, 
  gradeCollege: Secondary, 
  dataCollege:[{         
                 course: "Maths",
                 schedule: "Afternoon",
                 section: "A",
                 teacher: "Mateo",
               }
              ]
  }

Or with two elements:

{ name: Sara,
  age: 14, 
  gradeCollege: Secondary, 
  dataCollege:[{         
                 course: "Maths",
                 schedule: "Afternoon",
                 section: "A",
                 teacher: "Mateo",
               },
{         
                 course: "Language",
                 schedule: "Afternoon",
                 section: "C",
                 teacher: "Alex",
               }
              ]
  }

Now that you have an array of objects, it's recommended to use a FormArray of FormGroups for managing them effectively.

A FormArray of FormGroup can be handled consistently by following these steps:

1. Declare a getter function in your .ts file for the formArray:

get dataCollege()
{
    return this.form.get('dataCollege') as FormArray
}

2. In your .html file:

<form [formGroup]="form">
   ...input fields belonging to the main form should utilize formControlName...
   ..be sure to use formControlName="control-name"
   ..with control-name inside quotes but formControlName outside..

     <input formControlName="name">
       ....
   ..a div with formArrayName..
   <div formArrayName="dataCollege">

      ..use ngFor to iterate over the formArray.controls.. 
      ..employ the getter defined in the first step..
      ..add [formGroupName]="i"..

      <div *ngFor="let group of dataCollege.controls; let i=index"
                  [formGroupName]="i"

          ..similarly to the main group, use formControlName within the...
          ..FormArray for its controls..., e.g.

          <input formControlName="course">

      </div>
   </div>

And no need for additional getters to access the control anymore

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

a more refined method of retrieving 6 bytes from memory

I'm currently running code to extract a specific value that is 6 Bytes into an array. However, I find the current implementation to be somewhat unattractive. The code is being executed on a Little Endian Processor. Is there a more graceful way to achi ...

Populate choices in the mat-select dropdown

I am encountering an issue with two-way binding in the mat-select component from Angular Material. In my TypeScript file, I have an option list and a variable for binding called valueFromDB. When I choose a value in the mat-select component, it automatical ...

Guide to resolving the "Unknown provider" error in AngularJS

I encountered an error when using ui-router with a resolve, which resulted in an "Unknown provider" message. The error details displayed on the browser console are as follows: Error: [$injector:unpr] Unknown provider: appFacilityListProvider <- app ...

How can I move the cursor to the beginning of a long string in Mat-autocomplete when it appears at the end?

I'm struggling to figure out how to execute a code snippet for my Angular app on Stack Overflow. Specifically, I am using mat-autocomplete. When I select a name that is 128 characters long, the cursor appears at the end of the selected string instead ...

Implementing dynamic progress bar updates in an Angular application using Bootstrap

Seeking to create a progress bar in a Word object for a language vocabulary training app, displaying the number of correct and wrong answers. <div class="progress"> <div class="progress-bar progress-bar-striped progress-bar-a ...

Is there a proper way to supply createContext with a default value object that includes functions?

As I was creating my context, I set an initial state and passed the necessary functions for useContext. Although this method is functional, I'm concerned it may present challenges in larger projects. Does anyone have suggestions for a more efficient a ...

Sharing details of html elements using ng-click (AngularJS)

I am currently exploring options to enable users to click on a "open in new tab" link, which would essentially transfer that HTML element into a fresh window for their convenience. I am seeking advice on how to achieve this. At the moment, I am able to la ...

Tips for specifying a custom type as the return value of a function and denote it as a promise

I encountered this code snippet: import * as Promise from 'bluebird'; import {Response} from '../commonInterfaces/httpService'; getCall(url: string, accessToken: string, extraHeaders: object): Promise<Response> { let headers ...

Adjust each module import to accommodate a singleton dependency

I encountered a scenario in my application that involves the use of an ApiModule: Within this module, there are two services - ApiRouteService and ApiRouteLoaderService, both scoped to the module itself. The purpose of these services is as follows: ApiRo ...

Tips for retrieving additional values from a chosen variable in Angular 10

Here is an array I have: export const Glcode = [ { id: 1, Type: 'Asset', Name: 'Cash at Head Office', code: '10018' }, { id: 2, Type: 'Asset', Name: 'POS ACCOUNT ', code: '10432' }, { ...

AngularTS regex that enforces the use of a decimal point in numbers

I am working on a requirement where the input should only accept decimal characters, negative or positive. I need to use regex to make the decimal point mandatory, however it is currently allowing negative whole numbers which is not the desired behavior. I ...

What is the method for implementing a dropdown box with select and non-select checkboxes, similar to the example shown in the image, using AngularJS?

https://i.sstatic.net/CTx8K.jpg I am seeking assistance in incorporating the image below into a dropdown menu using angularjs ...

Unable to run TypeScript on Ubuntu due to compilation errors

throw new TSError(formatDiagnostics(diagnosticList, cwd, ts, lineOffset)) ^ TSError: ⨯ Unable to compile TypeScript Cannot find type definition file for 'jasmine'. (2688) Cannot find type definition file for 'node'. (2688) api/ ...

Accessing unique identifiers from Firebase Database with AngularFire

My Firebase Database is structured as shown below: fire-demo-123 course 1 : "course1" 2 : "course2" 3 : "course3" -MzOn2s : "course4" I am currently using the following code to fetch the list component.t ...

Managing status in Angular applications

I am currently working on a project using Angular 7 and I have the following code snippet: public deleteId(pId){ return this.http.delete<any>(this.deleteUrl(pId), {observe: 'response'}) .pipe(catchError(this.handleError)); } I ...

A TypeError was not captured when attempting to access information about Ionic1 through the angular.module

For the past 48 hours, I've been struggling with an issue that's really getting on my nerves. If anyone knows how to fix this, it would be greatly appreciated: I'm working on an Ionic 1 project and need to make some updates, but nothing I t ...

Convert the information from a geojson file into a different format for data presentation

In my possession is a .geojson file that I am aiming to decode using a PHP script based on the labeling within each array. There are six specific categories with labels: HIGH MDT ENH SLGT MRGL TSTM The task at hand involves breaking down this file into i ...

What is the best way to navigate back to the previous page while retaining parameters?

Is there a way to return to the previous page with specific parameters in mind? Any suggestions on how to achieve this? import {Location} from '@angular/common'; returnToPreviousPage(){ this._location.back(); } What I am looking ...

Unable to shrink array within an object

I'm encountering an issue while trying to reduce an array within an object. The error message I receive is: push is not a function To begin, I initialized my arrays as empty and created an add function to use as the first argument: function add(a,b ...

When the session times out in Angular 5, the previous user's credentials remain on the page instead of being replaced with the current user's information

When switching from one user to another in Angular 5, I am facing an issue where the previous user's credentials are displayed instead of the current user's until I refresh the page. I have tried using the localstorage.clear() method but it doesn ...