22

I've ran into some trouble setting the value of an input element using Angular.

I'm trying to set the value of dynamically created input elements in my application by this method:

copyPricingToAll(): void {
  var copyValue: string = document.getElementById("priceInputGlobal").value;

  this.currentItem.orderLines.forEach(orderLine => {
  document.getElementById("priceInput-" + orderLine.id).setAttribute("value", copyValue);
   });
  }

I'm creating the rows like this:

<ng-container *ngFor="let orderLine of currentItem.orderLines let i=index">
    <tr>
       <td>{{getCorrectTimeString(orderLine)}}</td>
       <td>{{orderLine.quantity}}</td>
       <td><input id="priceInput-{{orderLine.id}}" type="number" value="{{orderLine.price}}"></td>
    </tr>
</ng-container>

Unfortunately .value is not recognized as a valid operation. I'm not sure how to correctly set the value of a dynamically created element in angular. I hope someone is able to help me out with this issue.

Mikey123
  • 1,201
  • 2
  • 12
  • 23

4 Answers4

49

You should use the following:

<td><input id="priceInput-{{orderLine.id}}" type="number" [(ngModel)]="orderLine.price"></td>

You will need to add the FormsModule to your app.module in the imports section as follows:

import { FormsModule } from '@angular/forms';

@NgModule({
  declarations: [
    ...
  ],
  imports: [
    BrowserModule,
    FormsModule
  ],
  ..

The use of the brackets around the ngModel are as follows:

  • The [] show that it is taking an input from your TS file. This input should be a public member variable. A one way binding from TS to HTML.

  • The () show that it is taking output from your HTML file to a variable in the TS file. A one way binding from HTML to TS.

  • The [()] are both (e.g. a two way binding)

See here for more information: https://angular.io/guide/template-syntax

I would also suggest replacing id="priceInput-{{orderLine.id}}" with something like this [id]="getElementId(orderLine)" where getElementId(orderLine) returns the element Id in the TS file and can be used anywere you need to reference the element (to avoid simple bugs like calling it priceInput1 in one place and priceInput-1 in another (if you still need to access the input by it's Id somewhere else).

Oriol Roma
  • 329
  • 1
  • 5
  • 9
William Moore
  • 3,844
  • 3
  • 23
  • 41
  • Thank you for your reply. How do I access the ngModel value in code? – Mikey123 Jun 29 '18 at 08:06
  • 1
    Hi Mikey123, because of the two-way binding the `orderLine` object is automatically updated when the input field is updated. – William Moore Jun 29 '18 at 08:10
  • 1
    ngModel is deprecated from angular 7. – Mujthaba Ibrahim Sep 17 '19 at 06:52
  • @MujthabaIbrahim Only support for using the ngModel input property and ngModelChange event with reactive form directives has been deprecated in Angular v6. NgModel itself can still be used by using template-driven forms for example, see: [https://angular.io/api/forms/FormControlName](https://angular.io/api/forms/FormControlName) – Mikey123 Mar 24 '20 at 10:04
14

As an alternate you can use reactive forms. Here is an example: https://stackblitz.com/edit/angular-pqb2xx

Template

<form [formGroup]="mainForm" ng-submit="submitForm()">
  Global Price: <input type="number" formControlName="globalPrice">
  <button type="button" [disabled]="mainForm.get('globalPrice').value === null" (click)="applyPriceToAll()">Apply to all</button>
  <table border formArrayName="orderLines">
  <ng-container *ngFor="let orderLine of orderLines let i=index" [formGroupName]="i">
    <tr>
       <td>{{orderLine.time | date}}</td>
       <td>{{orderLine.quantity}}</td>
       <td><input formControlName="price" type="number"></td>
    </tr>
</ng-container>
  </table>
</form>

Component

import { Component } from '@angular/core';
import { FormGroup, FormControl, FormArray } from '@angular/forms';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  name = 'Angular 6';
  mainForm: FormGroup;
  orderLines = [
    {price: 10, time: new Date(), quantity: 2},
    {price: 20, time: new Date(), quantity: 3},
    {price: 30, time: new Date(), quantity: 3},
    {price: 40, time: new Date(), quantity: 5}
    ]
  constructor() {
    this.mainForm = this.getForm();
  }

  getForm(): FormGroup {
    return new FormGroup({
      globalPrice: new FormControl(),
      orderLines: new FormArray(this.orderLines.map(this.getFormGroupForLine))
    })
  }

  getFormGroupForLine(orderLine: any): FormGroup {
    return new FormGroup({
      price: new FormControl(orderLine.price)
    })
  }

  applyPriceToAll() {
    const formLines = this.mainForm.get('orderLines') as FormArray;
    const globalPrice = this.mainForm.get('globalPrice').value;
    formLines.controls.forEach(control => control.get('price').setValue(globalPrice));
    // optionally recheck value and validity without emit event.
  }

  submitForm() {

  }
}
sabithpocker
  • 15,274
  • 1
  • 42
  • 75
  • For situations where it's helpful to set the value of a reactive forms input control from the html, you can use [value]= and retrieve the value (optionally) from a property you define (so you can handle things like what to display for null values, etc). . Note: If you use ngModel on a reactive forms control, you can get a console error "Support for using the ngModel input property and ngModelChange event with reactive form directives has been deprecated." – StackOverflowUser May 16 '22 at 05:47
  • I prefer either setting and managing values from controller or from view, wouldn't mix both as it can grow into unreadable code pretty fast in my experience. – sabithpocker May 17 '22 at 19:37
  • 1
    Was about to reply "well, how can something in the view lead to unreadable code when it's very straightforward and declarative", but noticed what seemed to be working great yesterday wasn't working at all today. I realized there were timing issues - the view was pulling the property values b4 controller fields were getting populated. I removed the [value]= stuff from the view & used the properties from within the controller code where the fields were populated and then everything worked just fine. Which is a 500-word-essay way of saying dammit man were you correct. – StackOverflowUser May 18 '22 at 09:27
8

You can use 3 ways. They are,

1) Using ngModel

<input placeholder="Search..." autocomplete="off" [(ngModel)]="customSearch"/>

customSearch: string;
this.customSearch: string = "";

2) Using formControlName

<form [formGroup]="searchForm">
<input placeholder="Search..." autocomplete="off" formControlName="customSearch"/></form>

this.searchForm = this._fb.group({
  'customSearch': ['']
 });

this.searchForm.controls['customSearch'].setValue('');
let val = this.searchForm.value.customSearch;

3) Using ElementRef

<input placeholder="Search..." autocomplete="off" #customSearch />

@ViewChild('customSearch', { static: true }) customSearchElement: ElementRef;
this.customSearchElement.nativeElement.value = "";
Muthulakshmi M
  • 651
  • 7
  • 8
1
let test : any = document.getElementById("test") as HTMLInputElement | null
test.value = spaceNotes

This is another way to do it..