Can not send input data when using ngIf

I'm creating an app that allows users to add categories and reorder them. When pressing "add category" it creates a new row in the table and an input form displays to allow the user to name that category. However, I can not send the form data to the 'save' button if that input has *ngIf within it. I even tried putting the if statement on a div surrounding the input to no avail. I'm just getting that the input is undefined.

categories.component.html

<div class="container table-responsive">
  <table class="table table-striped table-hover">
    <thead>
      <p class="ml-3 mt-2 mb-0 float-left">Section</p>
      <a href="#" class="add-category mt-3 mr-3" (click)="addCatagory()">+ ADD CATEGORY</a>
      <tr>
        <th scope="col"></th>
        <th scope="col">CATEGORY</th>
        <th scope="col" class="mt-3">SEQ.</th>
        <th scope="col"></th>
      </tr>
    </thead>
    <tbody dragula="categories" [(dragulaModel)]="sortableList" id="tbody">
      <tr *ngFor="let row of sortableList; index as i;">
        <th scope="row" class="sort-cell">
          <button class="btn bg-transparent">
            <img src="/assets/images/sortable.svg" alt="">
          </button>
        </th>
        <td>
          <p *ngIf="!row.editing">
            {{row.title}}
          </p>

          <input #box placeholder="Category Name">

        </td>
        <td>
          <p class="seq text-white text-center">{{i + 1}}</p>
        </td>
        <td class="text-right">
          <button class="btn bg-transparent border-0" (click)="delCatagory(row.title)">
            <img *ngIf="!row.editing" src="/assets/images/delete.svg" alt="">
          </button>
          <a *ngIf="row.editing" href="#" class="add-category mt-3 mr-3" (click)="senddata(box.value, i)">Save</a>
        </td>
      </tr>
    </tbody>
  </table>
</div>

categories.component.ts

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-categories',
  templateUrl: './categories.component.html',
  styleUrls: ['./categories.component.scss']
})
export class CategoriesComponent implements OnInit {
  sortableList = [];

  senddata(value, i): void {
    var a = value;
    var current = this.sortableList.length;
    console.log('Value ' + a);
    console.log('Current ' + current);
    this.sortableList.splice(i, 1, {
      id: current,
      title: a,
      editing: false
    });
  }

  addCatagory(): void {
    var current = this.sortableList.length;
    this.sortableList.push({
      id: current + 1,
      title: 'Catagory ' + (current + 1),
      editing: true
    });
  }

  delCatagory(a): void {
    var pos = this.sortableList
      .map(function(e) {
        return e.title;
      })
      .indexOf(a);
    this.sortableList.splice(pos, 1);
  }

  spliceCategory(a): void {
    var value = a;
    var current = this.sortableList.length;
    console.log('Value ' + value);
    console.log('Current ' + current);
  }

  editCategory(a): void {
    var value = a;
    var current = this.sortableList.length;
    this.sortableList.push({
      id: current + 1,
      title: 'Catagory ' + (current + 1),
      editing: false
    });
  }

  constructor() {}

  ngOnInit() {}

  ngAfterViewInit() {}
}

Error

ERROR TypeError: "_co.box is undefined"
View_CategoriesComponent_5ng:///AppModule

/CategoriesComponent.ngfactory.js:28:11
handleEventhttp://localhost:4200/vendor.js:41342:16
callWithDebugContexthttp://localhost:4200/vendor.js:42435:22
debugHandleEventhttp://localhost:4200/vendor.js:42138:12
dispatchEventhttp://localhost:4200/vendor.js:38801:16
renderEventHandlerClosurehttp://localhost:4200/vendor.js:39245:38
decoratePreventDefaulthttp://localhost:4200/vendor.js:51364:36
invokeTaskhttp://localhost:4200/polyfills.js:2743:17
onInvokeTaskhttp://localhost:4200/vendor.js:34899:24
invokeTaskhttp://localhost:4200/polyfills.js:2742:17
runTaskhttp://localhost:4200/polyfills.js:2510:28
invokeTaskhttp://localhost:4200/polyfills.js:2818:24
invokeTaskhttp://localhost:4200/polyfills.js:3862:9
globalZoneAwareCallbackhttp://localhost:4200/polyfills.js:3888:17 
CategoriesComponent.html:35:10

1 answer

  • answered 2018-08-09 15:14 Jim P.

    I had this problem and if I am not mistaken: the problem is when sending data with inputs they need to have unique IDs to be distinct one from another during the different operations.

    Since the inputs are created with *ngFor, you can put a generic name in the ID attribute and append the index value of the *ngFor:

    <tbody dragula="categories" [(dragulaModel)]="sortableList" id="tbody">
              <tr *ngFor="let row of sortableList; index as i;">
                <th scope="row" class="sort-cell">
                  <button class="btn bg-transparent">
                    <img src="/assets/images/sortable.svg" alt="">
                  </button>
                </th>
                <td>
                  <p *ngIf="!row.editing">
                   {{row.title}}
                  </p>
    
                  <input #box placeholder="Category Name" id="CategoryName{{i}}">
    
                </td>
                <!-- ... -->
              </tr>
            </tbody>
    

    All input IDs will start with the same characters and have a unique number at the end, thus making them unique:

    <input #box placeholder="Category Name" id="CategoryName0">
    <input #box placeholder="Category Name" id="CategoryName1">
    ...
    <input #box placeholder="Category Name" id="CategoryNameN">