简体   繁体   中英

Angular @viewChild element's "nativeElement: and "value" are undefined

I am using an @ViewChild reference in my component:

@ViewChild('tableCellWidget', { static: false }) tableCellWidget: ElementRef<HTMLInputElement>;

And I am attempting to access the "value" of the element in which I have attached the tableCellWidget viewChild reference to in order to dynamically set the [innerHTML] of the trailing span element to the current value of the form element loaded via <select-widget-widget> template below (this is inside a loop of a dynamically generated mat-table:

   <td mat-cell *matCellDef="let data; let rowIndex = index"> 
    <ng-container *ngIf="column !== ACTIONS_COLUMN_NAME">
          <div *ngIf="layout[i] && layout[i].widget">
            <select-widget-widget
              #tableCellWidget
              [class]="dragDropEnhancedMode && childTemplateModel ? getActiveItemClass(childTemplateModel[i]) : ''"
              (change)="onCellChange($event, rowIndex, column)"
              [logic]="layout[i]?.options?.logic || {}"
              [rowIndex]="rowIndex + paginationRowOffset"
              [dataIndex]="[].concat(rowIndex + paginationRowOffset)"
              [layoutIndex]="(layoutIndex || []).concat(i)"
              [layoutNode]="layout[i]">
            </select-widget-widget>
            <span *ngIf="tableConfig?.columnTypes[column] === 'default'" [innerHTML]="getPlainText()"></span>
          </div>
    </ng-container>
  </td>

Here is the code for the innerHTML call:

getPlainText() {
  const myValue = this.tableCellWidget?.nativeElement?.value || 'no value';
  const myValue2 = this.tableCellWidget?.nativeElement;
  console.log('tableCellWidget value', myValue); // UNDEFINED
  console.log('tableCellWidget nativeElement', myValue2); // UNDEFINED
  console.log('this.getPlainTextHasValue', this.getPlainTextHasValue); // FALSE
  return myValue;
}

get getPlainTextHasValue(): boolean {
  // returns false;
  return hasValue(this.tableCellWidget?.nativeElement?.value);
}

The logs are all returning undefined and false as seen in the code. Here is an example of the actual DOM html I'm working with and the element I am trying to use to set the innerHTML on the span from:

 <div _ngcontent-sen-c24="" class="ng-star-inserted">
  <!--bindings={
      "ng-reflect-ng-if": "false"
      }-->
  <select-widget-widget _ngcontent-sen-c24="" ng-reflect-layout-node="[object Object]" ng-reflect-layout-index="0,1" ng-reflect-data-index="0" ng-reflect-row-index="0" ng-reflect-logic="[object Object]" class="">
      <!---->
      <hidden-widget _nghost-sen-c37="" class="ng-star-inserted">
        <input _ngcontent-sen-c37="" type="hidden" value="Rob Bischoff-4" ng-reflect-form="[object Object]" id="control1660760198229_1660760203705" name="accountName" class="ng-untouched ng-dirty ng-valid"><!--bindings={
            "ng-reflect-ng-if": "false"
            }-->
      </hidden-widget>
  </select-widget-widget>
</div>

Any help much appreciated. There's obviously something missing from my approach.

Update: I have another viewChild element in my component. It appears that when I compare the console.logs for that element and my element, I can see that they are much different. It appears that my viewchild is referencing the angular template vs the underlying native element it represents? From the pic below you can see the log output for this.filterRef vs this.tableCellWidget. With this its obvious to me why its undefined but not obvious how I can obtain the reference to the underlying element instead.

在此处输入图像描述

It's because you're referencing a component, not an HTML element. Angular is designed such that components are "black boxes" to each other. The Angular way is to have the child emit the value to the parent, not directly query the value through html.

I don't know the internal workings of select-widget-widget so I can't show you exactly what to do, but it should have an event emitter that emits that value whenever it changes. Then you just have a property in the parent you update on change. Perhaps (change) is already doing this, and you just need to update the property in onCellChange() .

Example:

<select-widget-widget
  (valueChange)='widgetValue = $event'
  ...
></select-widget-widget>
<span [innerHTML]="widgetValue"></span>
// Component TS
widgetValue = ''

or if change is emitting the value already:

<select-widget-widget
  (change)="onCellChange($event, rowIndex, column)"
  ...
></select-widget-widget>
<span [innerHTML]="widgetValue"></span>
widgetValue = '';

onCellChange(ev, row, col) {
  this.widgetValue = ev;
  ...
}

But of course you don't have to do things the Angular way. You can always just use vanilla JS.

<select-widget-widget
   id='tableCellWidget'
   ...
></select-widget-widget>
<span [innerHTML]="tableCellWidget?.value"></span>
tableCellWidget?: HTMLInputElement;

ngAfterViewInit() {
  this.tableCellWidget = document.querySelector(
    '#tableCellWidget > hidden-widget > input'
  );
}

Warning: View encapsulation goes out the window with this method. If there are mutliple widgets, you need to ensure they all have a unique id or you're gonna have a bad time. You can just append something unique to the id, like the column / row numbers.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM