[Angular2] Child template view not updating when modifying values from Observable in parent.

9,677 views
Skip to first unread message

Andrew Ip

unread,
Dec 19, 2016, 2:23:10 PM12/19/16
to Angular
Hi all,

I've got a parent component that subscribes to an Observable that returns some data.
That Observable also sets some data in the parent that is passed to a child component.

I'm having an issue though, when the parent's Observable modifies X, and X is passed to the child, the child template does not show the modified value of X.

I can provide code if need be, but this is a general question about parent->child value propagation via an Observable.

Thanks in advance

Lucas Lacroix

unread,
Dec 19, 2016, 2:31:22 PM12/19/16
to Angular
It would definitely help to see a code example.

My best guess, without seeing any code, is that the parent component's template is bound to the observable itself while the child is bound so some value returned by the observable and, therefore, does not know when the parent's value has changed. Similar issues can occur if the parent is bound to 'x', such that the value of 'x' is:
{
  title: 'Titular Title',
  data: ...
}
and the child is bound to x.title. If you change x in the parent, the child does not know to re-render because it only has a reference to x.title and not x. If you were to change x.title, the child would re-render.

-Luke

--
You received this message because you are subscribed to the Google Groups "Angular" group.
To unsubscribe from this group and stop receiving emails from it, send an email to angular+u...@googlegroups.com.
To post to this group, send email to ang...@googlegroups.com.
Visit this group at https://groups.google.com/group/angular.
For more options, visit https://groups.google.com/d/optout.
--
Lucas Lacroix
Computer Scientist
Advanced Technology Division, MEDITECH

Andrew Ip

unread,
Dec 20, 2016, 7:40:14 AM12/20/16
to Angular
You're right, the Observable is bound to the parent, that may be why change detection is not propagating to the child who's receiving the end-value of the Observable.

I'll try changing the way I'm passing the values as you've mentioned and get back with the results.

Thanks for the quick reply!

Andrew Ip

unread,
Dec 20, 2016, 8:16:59 AM12/20/16
to Angular
So it didn't work in the end.

I'll provide some code as to have a better understanding of what's going on.

For clarification's sake I'm also using bindings from AmazeUI JS for toggling events when I collapse and expand a div row.


PARENT COMPONENT

import { Component, Input, Output, OnInit, AfterViewInit, OnDestroy, ViewChild, EventEmitter } from '@angular/core';
import { ParentService } from '../services/parent.service';
import { Observable } from 'rxjs/Rx';
import { ChildComponent } from '../child.component';

@Component({
  selector: 'app-parent',
  templateUrl: './parent.component.html',
  styleUrls: ['./parent.component.scss']
})

export class ParentComponent implements OnInit, AfterViewInit, OnDestroy {
  intervalId;
  parentService;
  dataSubscription;
  syncData = {
    val1: 0,
    val2: 100
  }

  @Input() model = {};
  @Input() index;

  // parentCollapse is the name of the template variable #parentCollapse on parent element
  @ViewChild('parentCollapse') parentCollapseChild;
  @ViewChild(ChildComponent) childComponent;

  ngAfterViewInit ()   {
    $(this.parentCollapseChild.nativeElement).on( 'open.collapse.amui', this.openCollapsibleParentHandler.bind(this));
    $(this.parentCollapseChild.nativeElement).on( 'close.collapse.amui', this.closeCollapsibleParentHandler.bind(this));
  }

  constructor(parentService: ParentService) {
    this.parentService = parentService;
  }

  openCollapsibleParentHandler (event) {
      if ( Object.is(this.parentCollapseChild.nativeElement, event.target) ) {
        this.dataSubscription = this.showRealTimeData();
      }
      return true;
    };

  closeCollapsibleParentHandler (event) {
      if ( Object.is(this.parentCollapseChild.nativeElement, event.target)) {
        this.dataSubscription.unsubscribe();
      }
      return true;
    };

  ngOnInit() {}

  showRealTimeData(pollingInterval: number = 1500) {
    return this.parentService.connectData(pollingInterval).subscribe(
      data => {
        this.syncData.val1 = data.val1;
        this.syncData.val2 = data.val2;
      }
    );
  };

  ngOnDestroy() {
    if(this.dataSubscription) {
      this.dataSubscription.unsubscribe();
    }
  }

}


<div id="parentCollapse-{{index}}" #parentCollapse user-parent-collapse class="am-panel-collapse am-collapse detailBelow" attr.auto-open-detail-collapse="{{index}}">
  <app-child [model]="model" [index]="index" [syncData]="syncData"></app-child>
</div>


CHILD COMPONENT

import { Component, OnInit, Input, AfterViewInit, ViewChild } from '@angular/core';
import 'rxjs/add/observable/interval';

@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  styleUrls: ['./child.component.scss']
})
export class ChildComponent implements OnInit {

  constructor() { }

  @Input() model;
  @Input() index;
  @Input() syncData: any;

  ngOnInit() {}

}

<div class="child">

  <div class="reach" >{{syncData.val1}}% success</div>
  <div class="rms">{{syncData.val2}} seconds</div>
   
</div>



I've removed unrelated code, but this is essentially how it's structured. And the connectData() method returns stubbed data which returns random numbers. I know the data works fine because it's only change detection that isn't triggered on the child, the data itself is fine and changes in the variables. And whenever I make a change in the page afterwards (like changing pages etc.) the value then changes.


Thanks for the help in advance!





On Monday, December 19, 2016 at 2:31:22 PM UTC-5, Lucas Lacroix wrote:

Lucas Lacroix

unread,
Dec 20, 2016, 8:34:03 AM12/20/16
to Angular
Ok. I did some searching and it looks like: unless the change to the data occurs in an event handler (which it is not in your code), the component will not perform change detection.

This article has some pointers for this scenario.

Andrew Ip

unread,
Dec 20, 2016, 8:49:30 AM12/20/16
to Angular
Thanks for the article. 

I got change detection to occur manually, but it doesn't seem very efficient to run an interval checker like that.

As you've suggested, is there any way bind the AmazeUI events to an Event Handler that is natively recognized by the Angular2 framework to work with change detection?

Lucas Lacroix

unread,
Dec 20, 2016, 8:53:37 AM12/20/16
to Angular
Don't run it in the interval. Run the check in the observable callback after you change the values like this:
 showRealTimeData(pollingInterval: number = 1500) {
    return this.parentService.connectData(pollingInterval).subscribe(
      data => {
        this.syncData.val1 = data.val1;
        this.syncData.val2 = data.val2;
        this.changeDetector.detectChanges();
      }
    );
  };

I really do not know anything about AmazeUI. But, regardless, your event triggers the start of the observable which means the observable will not have a new value within the scope of the event - it receives the value asynchronously.

Andrew Ip

unread,
Dec 20, 2016, 8:56:16 AM12/20/16
to Angular
Gotcha.

I'm using changeDetector.detectChanges now within the call and it seems to work fine.


Thanks for your assistance!
Reply all
Reply to author
Forward
0 new messages