24

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.

11
  • 1
    Can you post your HTML please? Commented Jun 29, 2018 at 7:47
  • 1
    are you calling this function after the view initialization or before? Commented Jun 29, 2018 at 7:49
  • 2
    You should use ngModel property instead of value : angular.io/api/forms/NgModel Commented Jun 29, 2018 at 7:55
  • 2
    Use [ngModel] instead of traversing the DOM, using getElementById while you can use data-binding is like using a spoon to cut your meat. Commented Jun 29, 2018 at 7:57
  • 1
    when this function copyPricingToAll() is called? Commented Jun 29, 2018 at 8:01

4 Answers 4

52

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).

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you for your reply. How do I access the ngModel value in code?
Hi Mikey123, because of the two-way binding the orderLine object is automatically updated when the input field is updated.
ngModel is deprecated from angular 7.
@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
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() {

  }
}

3 Comments

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). <input formControlName="price" [value]="myproperty">. 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."
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.
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.
9

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 = "";

Comments

1
let test : any = document.getElementById("test") as HTMLInputElement | null
test.value = spaceNotes

This is another way to do it..

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.