maraxai
maraxai

Reputation: 145

Angular 8 - in my simple example, ViewChild() works only with an array but not with a variable

How can I get this working: I have a child component with an input field and a SEND button. On click the value should be rendered in the parent component.

This setup works: The child component has an input that uses ngModel double-binding for the string. The button fires a function that pushes the current value of the input field into an array. The parent component uses ViewChild() and through a *ngFor loop every new value is rendered in the list.

How can I implement this without an array but just a single value variable? The code that is commented-out (for array setup) works. Stackblitz: https://stackblitz.com/edit/ng8child2parentviewchildtest?embed=1&file=src/app/app.component.ts

Any help is appreciated, thanks.

Child Component
template:
<input type="text" [(ngModel)]="currentMsgToParent">
<button (click)="msgToParent()">send</button>

.ts:
export class Child1Component {
  name = 'Child';

  currentMsgToParent = '';
  // msgFromChild1 = []

  msgToParent() {
    this.currentMsgToParent;
    // this.msgFromChild1.push(this.currentMsgToParent);
  }
}

parent component:
template:
<div class="child1">
  <p><b>Message from Child</b><br> via ChildView():</p>
  <p>{{ currentMsgToParent }}</p>
  <!-- <ul *ngFor="let item of msgFromChild1">
    <li>{{ item }}</li>
  </ul> -->
  <hr>
  <app-child1></app-child1>
</div>

ts.:
export class AppComponent implements AfterViewInit {
  @ViewChild(Child1Component, {static: false}) child1: Child1Component;

  name = 'Parent';
  msgFromChild1: any;
  currentMsgToParent = '';

  ngAfterViewInit() {
    this.currentMsgToParent = this.child1.currentMsgToParent;
    // this.msgFromChild1 = this.child1.msgFromChild1;
  }
}

Upvotes: 0

Views: 2440

Answers (2)

Martin Parenteau
Martin Parenteau

Reputation: 73751

The problem is that you update the value in the parent component only once, in ngAfterViewInit:

ngAfterViewInit() {
  this.currentMsgFromChild = this.child1.currentMsgToParent;
}

Therefore, the parent view always displays the initial value, which is an empty string.

On the other hand, when the parent component property is set to an array of the child component, both properties refer to the same array. When the array is modified, the two components can access the updated values. You would have the same situation if the two components shared a reference to an object, with the child input bound to a property of that object (see this stackblitz).


The standard way to update the parent would be through event binding with an @Output property, but if you want to refer directly to the child component in code, you can define the parent component property as a getter. That would ensure that the value displayed in the view is up-to-date.

get currentMsgFromChild() {
  return this.child1 ? this.child1.currentMsgToParent : "";
}

See this stackblitz for a demo.

Upvotes: 1

KamLar
KamLar

Reputation: 428

There is no way to use ViewChild inside your ngFor loop in Html files. But there is another way, to do something similiar to it. You can use Directives and inside them use ViewChild. You can create Directive using ng generate directive directive_name and use ViewChild inside of it. You can also pass some attributes to this directive the same way, you do with components. But to use ViewChild you have to inject elementRef using constructor:

constructor(private elementRef: ElementRef)

will inject it.

Upvotes: 0

Related Questions