19

I am experimenting with Angular2 and Angular Material. I used *ngFor to let Angular generate the <input> elements for me. However, in the resulting webpage, the generated element does not have name attribute.

This is part of the code in order-form.component.html, which asks the user to input the number of different kinds of fruits:

<md-list-item>
  <md-icon md-list-icon>shopping_cart</md-icon>
  <md-input-container *ngFor="let fruit of fruits" class="fruit-input">
    <input mdInput [(ngModel)]="order[fruit]" [placeholder]="capitalize(fruit)" 
           [id]="fruit" name="{{fruit}}" required value="0" #fruitInput 
           (input)="onInput(fruitInput)">
  </md-input-container>
</md-list-item>

This is the corresponding order-form.component.ts:

import { Component, OnInit } from "@angular/core";
import { Order } from "app/order";
import { PAYMENTS } from "app/payments";
import { OrderService } from "app/order.service";

@Component({
  selector: 'app-order-form',
  templateUrl: './order-form.component.html',
  styleUrls: ['./order-form.component.css']
})
export class OrderFormComponent implements OnInit {

  order = new Order();

  payments = PAYMENTS;

  fruits: string[] = [
    'apples',
    'oranges',
    'bananas'
  ];

  constructor(public service: OrderService) {
  }

  ngOnInit() {
  }

  get totalCost() {
    return this.service.getTotalCost(this.order);
  }

  onFocus(element: HTMLElement) {
    element.blur();
  }

  onSubmit() {
    console.log('onSubmit');
  }

  onInput(element: HTMLInputElement) {
    console.log(element);
    if (!this.service.isValidIntString(element.value)) {
      window.alert(`Please input a correct number for ${element.name}`);
      element.value = '0';
    }
  }

  capitalize(str: string): string {
    return this.service.capitalize(str);
  }

  get debug() {
    return JSON.stringify(this.order, null, 2);
  }
}

In the Chrome browser, when I right click the 'apples' <input>, the name attribute of the element is empty, but the ng-reflect-name is set to apples correctly? How to resolve this problem? No name attribute here, but ng-reflect-name is apples

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
Chiu King Yee CKY
  • 195
  • 1
  • 1
  • 6

1 Answers1

33

final answer

Use ([name]="fruit" or name="{{fruit}}") and ([attr.name]="fruit" or attr.name="{{fruit}}") together will work.

update

If you want to use the string 'fruit' as value for the name attribute, use

name="fruit"

or

name="{{'fruit'}}"

or

[name]="'fruit'"

otherwise you bind the value of the components field fruit (which your component doesn't seem to have)

original

Angular does property binding by default. If you want attribute binding you need to make that explicit

 attr.name="{{fruit}}" (for strings only)

or

 [name]="fruit"

See also

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • When i use `attr.name="{{fruit}}"` the browser will give me error: If ngModel is used within a form tag, either the name attribute must be set or the form control must be defined as 'standalone' in ngModelOptions. When I use `[name]="fruit"` the result is not change at all. – Chiu King Yee CKY Mar 18 '17 at 18:45
  • Then the problem is something else. So you don't actually care about the `name` attribute being added to the ``, you just want the `ngModel` to work? – Günter Zöchbauer Mar 18 '17 at 18:46
  • I think you actually want `name="fruit"` – Günter Zöchbauer Mar 18 '17 at 18:47
  • `ngModel` is working. No issue. When I key in any thing the bottom json string will change accordingly. What I do want is to add the `name` attribute into the `input` element. Thank you! – Chiu King Yee CKY Mar 18 '17 at 18:48
  • Sorry, what I want is for 'apples', the `name` is `apples`, for `oranges`, the name is `oranges`, etc. If I change `name="fruit"`, the `name` attribute is still not shown. – Chiu King Yee CKY Mar 18 '17 at 18:49
  • Sorry, I missed the `fruit` in `*ngFor`. Still, in this case my original answer is to get the attribute `name` added to the DOM. If `ngModel` doesn't work this way you might need both `attr.name="{{fruit}}" name="{{fruit}}"` (but I would find this weird) – Günter Zöchbauer Mar 18 '17 at 18:51
  • Thank you! It works when I put both `name={{fruit}}` and `attr.name={{fruit}}`. But why? – Chiu King Yee CKY Mar 18 '17 at 18:53
  • To add the attribute you need `attr.name={{fruit}}`. `ngModel` somehow doesn't seem to take the attribute and depends on a property binding. Sounds like a bug to me. – Günter Zöchbauer Mar 18 '17 at 18:54
  • But the `fruit` is not from `ngModel`. It's just the variable used in `*ngFor`, i.e., in the `` I have `*ngFor="let fruit of fruits"`. I also think it's kind of a bug. Anyways, could you change your answer to using both so that I can set it as the correct answer? – Chiu King Yee CKY Mar 18 '17 at 18:56
  • I don't think that is a bug. If we use template expression like `name={{fruit}}` then it is only property binding. If we use one time binding like `name="constantString"` then it will set attribute and also provide Input binding to directive. Using `attr.name` is right way i think `[attr.name]="fruit"` should also work – yurzui Mar 18 '17 at 18:59
  • @yurzui If I only use `attr.name` Angular will just complain an error to me. I have to use both. – Chiu King Yee CKY Mar 18 '17 at 19:00
  • Yes, you have to use both because ngModel requires `@Input() name` – yurzui Mar 18 '17 at 19:00
  • `attr.name="{{...}}"` is passed to `@Input() name` as well. It has to be something else. – Günter Zöchbauer Mar 18 '17 at 19:04
  • @yurzui Can you be more specific on `@Input() name`? Like how do I change my component so that I can use `[attr.name]="fruit"` only? – Chiu King Yee CKY Mar 18 '17 at 19:06
  • https://plnkr.co/edit/Mc00Msri3fxwTskGeSxx?p=preview `attr.name="{{..}}"` is not passed to `@Input name` – yurzui Mar 18 '17 at 19:09
  • Is it because of the Angular2 material library? Because when I change your code to simply using `name="{{fruit}}"`, the `name` attribute is there. When I try to create a new `` at the bottom using the same `*ngFor` and same `[name]="fruit"` the `name` attribute is correctly shown. – Chiu King Yee CKY Mar 18 '17 at 19:19
  • I just found that if I remove `[(ngModel)]` it will work with single `[name]="fruit"`. But once I add back `[(ngModel)]` the `name` is gone. – Chiu King Yee CKY Mar 18 '17 at 19:24
  • @saghar.fadaei is `fruit` the name of a variable or the string literal you want to use? In the later case you should use `name="fruit"` – Günter Zöchbauer Jun 24 '18 at 12:12
  • For `[attr.name]="value"` and `[name]="value"`, what is the difference? – FindOutIslamNow Jul 01 '18 at 10:16
  • The later will cause an exception if the HTML element does not have a property with this name and also not hosts an Angular component or directive that does not have an `@Input() name;` . See also https://stackoverflow.com/questions/6003819/what-is-the-difference-between-properties-and-attributes-in-html – Günter Zöchbauer Jul 01 '18 at 11:56