How can I dynamically assign the formControlName to an <input> element within Angular?

I'm currently developing a component with the goal of streamlining and standardizing the appearance and functionality of our forms. The code snippet below illustrates the structure:

Sample Implementation

...
<my-form-input labelKey = "email" controlName="emailAddress" [form]="userForm">
    <input myInput class="form-control" type="email" formControlName="emailAddress" />
</my-form-input>
...

In the code snippet above, "emailAddress" is passed to MyFormInputComponent as the controlName and is then passed again to the FormControlName directive on the <input> element. I would like to streamline this process by only passing it once to enhance user experience.

Is there an effective way to achieve this, or should I simply accept this constraint? An explanation regarding the reason behind this constraint would be appreciated. The code is presented below.

I have explored two approaches:

  1. Implementing a
    @HostBinding("attr.formControlName")
    annotation in the MyInput component. While this method allows me to manipulate an attribute called
    formcontrolname</code, it does not trigger the Angular Forms directive required to properly register the control with the group.</li>
    <li>Requesting the user to provide <code>formControlName
    for the <input> element and extracting the value for the rest of the component. This approach may work, but it would involve accessing the DOM directly through an ElementRef, which is not recommended. The recommended method for DOM interaction -- Renderer -- does not appear to offer any attribute reading capabilities.

my-form-input.component.ts

@Component({
    selector: 'my-form-input',
    templateUrl: './my-form-input.component.html',
    styleUrls: ['./my-form-input.component.scss']
})
export class MyFormInputComponent implements OnInit, AfterContentInit {
    @Input()
    labelKey: string;

    @Input()
    controlName: string;

    @Input()
    form: FormGroup;

    @ContentChild(MyInputDirective)
    input: MyInputDirective;

    ngAfterContentInit(): void {        
        this.initInput();
    }

    /**
     * Updates the input we project into the template
     */
    private initInput() {
        this.input.updatePlaceholder(this.labelKey);
        // I would like to modify the FormControlName directive applied to the input here
    }
}

my-form-input.component.html

<label>{{ labelKey | translate }}</label>
<ng-content></ng-content>
<my-input-error [control]="form.controls[controlName]" [name]="labelKey | translate" />

my-input.directive.ts

@Directive({
    selector: '[myInput]'
})
export class myInputDirective implements OnInit {
    private placeholderKey = ""; 

    @HostBinding("placeholder")
    private placeholder: string;

    updatePlaceholder(placeholderKey: string) {
        this.placeholderKey = placeholderKey;
        this.placeholder = this.translateService.instant(this.placeholderKey);
    }

    constructor(private translateService: TranslateService) {
    }
}

my-form-error.component.ts

// Not shown since not relevant.

Answer №1

While I can't pinpoint the exact reasoning behind it, I have come to realize that I may have gone astray in my assumptions.

I previously believed that my component owned the elements it projected, but now I understand that this is not actually the case. The best way to set attributes or directives is in the template itself. It's more effective to include any elements that you want to control within the template rather than projecting them.

As a result, I have started creating separate components for specific form controls, such as <input> and <textarea>. This approach seems to be more efficient from a design perspective. Trying to wrap all possible form controls in one component was not practical. Instead, I either project a form control that duplicates some properties or create specific components for each form control. Sometimes, I do a combination of both -- turning common form controls into components and reserving the unique cases for projecting components.

Here are a few blogs that guided me in the right direction:

  1. https://medium.com/@vadimkorr/implementing-nested-custom-controls-in-angular-5-c115c68e6b88

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

Tips for sorting items in Wordpress with JavaScript / on click?

I am currently utilizing a method similar to the one outlined in this resource from w3 schools for filtering elements within divs. However, I am facing challenges when attempting to integrate this script into my Aurora Wordpress site due to the removal of ...

Tips for preventing real-time changes to list items using the onchange method

I am facing an issue with a list of items that have an Edit button next to them. When I click the Edit button, a modal pops up displaying the name of the item for editing. However, before clicking the save button, the selected item in the list gets changed ...

Update dataTable with new data fetched through an ajax call from a separate function defined in a different

I need to refresh the data in my datatable after using a function to delete an item. The function for deleting is stored in a separate file from the datatable. Here is the delete function code: function removeFunction(table,id) { var txt= $('.ti ...

Is it possible to deceive Array.isArray?

Although I have a good understanding of prototypes, I encountered some confusion when I attempted the following: var obj = {}; Object.setPrototypeOf(obj, Array.prototype); console.log(Array.isArray(obj)); // false? What's even more perplexing: var ar ...

Typescript throwing error TS2307 when attempting to deploy a NodeJS app on Heroku platform

Encountering an error when running the command git push heroku master? The build step flags an error, even though locally, using identical NodeJS and NPM versions, no such issue arises. All automated tests pass successfully without any errors. How can this ...

Pinia has not been instantiated yet due to an incorrect sequence of JavaScript file execution within Vue.js

I'm currently developing a Vue.js application using Vite, and I have a Pinia store that I want to monitor. Below is the content of my store.js file: import { defineStore } from 'pinia'; const useStore = defineStore('store', { st ...

Parallel ajax function

After creating a form for registering new accounts, I wanted to ensure that the chosen email is available by checking it against a webservice. However, this process takes a few seconds. Let's take a look at the method used: function validateEmail(ema ...

Creating a dynamic user interface with HTML and JavaScript to display user input on the screen

I'm currently working on creating an input box that allows users to type text, which will then appear on the screen when submitted. I feel like I'm close to getting it right, but there's a problem - the text flashes on the screen briefly bef ...

Show the total sum of a specific column in a datatable and enable the option to export the data to an Excel

When exporting a datatable as an Excel file with 4 columns, the third column contains product prices. After the export, I would like to see an additional row at the end of the table labeled "Total" that displays the sum of all values in column 3. I initia ...

Is there any benefit to fetching data in `{#await}` or `onMount` instead of `load` in SvelteKit, especially considering the impact on `+server`?

keyword: displaying loading spinner Imagine accessing a route like /dashboard, where most of the page content remains constant but certain sub-components require real-time data. If I retrieve this data within a load function, the entire route will remain ...

In order to use DIV jQuery, it is necessary to have at least one input

In my form, there are 5 input fields. On button click using JQuery, I need to validate that at least one of these inputs is filled out. <div id='myForm'> <input name="baz" type="text" /> <input name="bat" type="text" /> ...

Bringing a JavaScript function into a TypeScript file results in it being treated as a namespace

Trying to bring a vanilla JavaScript function into a TypeScript file within a React Native app has presented some challenges. The import process goes smoothly when working with JS, but switching to TS triggers the error message: Cannot use namespace &apos ...

Stop the change event from occurring on a textarea when the user clicks on an external cancel button

In a particular scenario, there is a textarea with an autosave feature triggered by the change event. When the textarea is focused on, Save and Cancel buttons appear at the bottom, providing users with options in case they prefer not to simply click outsid ...

Can someone guide me on incorporating bluebird promises with request-extensible?

I'm interested in utilizing the bluebird library to create a promise-based asynchronous web client. Currently, I have been using the request-promise package for this purpose. To get started, I simply include the following lines of code at the beginnin ...

Tips for customizing the appearance of the react-stripe-checkout form

Could someone please provide guidance on applying custom styles to the react-stripe-checkout form component, such as changing the background color? Visit this link for more information ...

Guide to automating tests on MAC using Protractor with Appium and IOS-Simulator

Is there a way to run automated tests using Protractor (or WebDriverJS) on a MAC with Appium and the IOS Simulator? We have been unsuccessful in running tests with the following configuration file, although it works fine with Selenium 2.0 - WebDriver. He ...

I am tasked with creating an onScroll function that identifies the largest HTML element visible on the screen by utilizing the IDS contained within an array

I am looking to create an onScroll function that can identify the largest HTML element currently visible on the screen, based on the IDs stored in an array. For example, if "section1" takes up more space on the screen than "section2" as I scroll down the ...

What is the best way to toggle dropdown menu items using jQuery?

Upon clicking on an li, the dropdown menu associated with it slides down. If another adjacent li is clicked, its drop down menu will slide down while the previous one slides back up. However, if I click on the same li to open and close it, the drop down m ...

Is it possible to use reactjs and react-router to showcase either a component or {this.props.children}?

Here's the scene: I have multiple components where certain words can be clicked to link to a reference page/component. If the highlighted words are not clicked, the component is displayed as is (and there are many of them with this feature and a menu ...

Issues with the script manager functionality in asp.net

I need to display a message to the user after submitting a form and include the ID received from the database in the message like this: int reqid = getIDFromDatabase(); string scrp = "<script>alert('Your request has been submitted successfully ...