如何将异步数据传递给对象中的子组件(Angular6)

时间:2018-06-21 10:13:52

标签: javascript angular rxjs observable angular6

我试图显示从服务器(使用Angular 6,Rxjs和Chartjs)检索到的数据,并使用该数据呈现图表。 如果我使用本地模拟数据,那么一切都会很好地呈现。但是,如果我使用从服务器获取数据,则无法提供渲染图形所需的数据,因此这些图表将显示为空白图表。

摘要: 组件进行服务调用,并使用该服务调用的响应准备要传递给子组件的对象。但是,到响应准备就绪时,该对象已经发送了,而没有必要的信息。

服务代码段

apply

在client-info.component.ts 中(用于进行服务调用并准备并将对象传递给子组件的组件)

  getAccountsOfClientId(clientID: string): Observable<Account[]> {
    return this.http.get<Account[]>(`${this.BASE_URL}/accounts?client=${clientID}`)
      .pipe(
        tap(accounts => console.log('fetched client\'s accounts')),
        catchError(this.handleError('getAccountsOfClientId', []))
      );
  }

我正在使用ngOnChanges(在子组件中)寻找更改,但是在“ clientAccounts”数组填充响应后,在子组件中更新图表数据。

@Input() client; // received from another component, data is filled

  constructor(private clientAccountService: ClientAccountService) { }

  ngOnInit() {
    this.getAccountsOfClientId(this.client.id);
  }

  ngAfterViewInit() {
    this.updateChart(); // render for pie chart
    this.updateBarChart(); // render for bar chart
  }

  getAccountsOfClientId(clientID: string): void {
    this.clientAccountService.getAccountsOfClientId(this.client.id)
      .subscribe(accounts => this.clientAccounts = accounts);
  }

  updateBarChart(updatedOption?: any): void {
    /* unrelated operations above ... */

    // Create new base bar chart object
    this.barChart = {};
    this.barChart.type = 'bar';
    this.setBarChartData();
    this.setBarChartOptions('Account', 'Balance');
  }

  setBarChartData(): void {

    // field declarations..

    console.log('clientAccounts has length: ' + this.clientAccounts.length); // prints 0
    this.clientAccounts.map((account, index) => {
        // do stuff
    });

    dataset = {
      label: 'Balance',
      data: data,
      ...
    };

    datasets.push(dataset);

    // since clientAccounts was empty at the time this function ran, the "dataset" object doesn't contain
    // the necessary information for the chart to render
    this.barChart.data = {
      labels: labels,
      datasets: datasets
    };
  }

您能指出我应该如何继续对此进行研究吗?

很长的问题,很抱歉,谢谢!

编辑:根据要求,模板位于以下

父母(客户信息)

@Input() chart: Chart;
@Input() canvasID: string;
@Input() accountBalanceStatus: string;

ngOnChanges(changes: SimpleChanges) {
  if (changes['accountBalanceStatus'] || changes['chart']) {
    this.renderChart();
  }
}


renderChart(): void {
  const element = this.el.nativeElement.querySelector(`#${this.canvasID}`);

  if (element) {
    const context = element.getContext('2d');

    if (this.activeChart !== null) {
      this.activeChart.destroy();
    }

    this.activeChart = new Chart(context, {
      type: this.chart.type,
      data: this.chart.data,
      options: this.chart.options
    });
  } else {
    console.log('*** Not rendering bar chart yet ***');
  }
}

图表:

<div class='client-info-container'>
  <div class='info-container'>
    <li>Date of Birth: {{ client.birthday | date: 'dd/MM/yyyy'  }}</li>
    <li>Name: {{ client.name }}</li>
    <li>First Name: {{ client.firstname }}</li>
  </div>

  <div class='more-button'>
    <button (click)='openModal()'>More</button>
  </div>

  <div class='chart-container'>
    <div *ngIf='pieChart && client'>
      <app-balance-pie-chart
      [chart]='pieChart' 
      [canvasID]='accountBalancePieChartCanvasID'
      (updateChart)='handlePieChartOnClick($event)'>
      </app-balance-pie-chart>
    </div>

    <div class='bar-chart-container'>
      <div class='checkbox-container'>
        <div *ngFor='let option of cardTypeCheckboxOptions' class='checkbox-item'>
          <input
          type='checkbox' 
          name='cardTypeCheckboxOptions' 
          value='{{option.value}}' 
          [checked]='option.checked'
          [(ngModel)]='option.checked'
          (change)="updateCardTypeCheckboxSelection(option, $event)"/>

          <p>{{ option.name }} {{ option.checked }}</p>
        </div>
      </div>

      <div *ngIf='barChart && client'>
        <!--  *ngIf='client.accounts.length === 0' -->
        <div class="warning-text">This client does not have any accounts.</div>
        <!--  *ngIf='client.accounts.length > 0' -->
        <div>
            <app-balance-account-bar-chart
            [chart]='barChart'
            [canvasID]='accountBarChartCanvasID'
            [accountBalanceStatus]='accountBalanceStatus'>
            </app-balance-account-bar-chart>
        </div>
      </div>

    </div>
  </div> 
</div>

5 个答案:

答案 0 :(得分:2)

我看到,您没有将数据直接分配给this.barChart,而是将其分配为this.barChart.data,这意味着您正在直接修改属性,而该属性可能不会调用{{1} }子组件。这是由于您在评论中给出的解释。

  

我读到这可能是因为角度变化检测检查了   通过查看对象引用来实现差异

     

并且不知道对象的属性何时更改

绑定到ngOnChanges属性的变量是@Input()而不是this.barChart

代替

this.barChart.data

您尝试这个

this.barChart.data = {
      labels: labels,
      datasets: datasets
};

在这里,您正在直接修改this.barChart = { data : { labels: labels, datasets: datasets }}; ,它将触发this.barChart

编辑:

您应该在

ngOnChanges()块内调用this.updateChart();
subscribe

这就是为什么您也将this.clientAccountService.getAccountsOfClientId(this.client.id) .subscribe((accounts) => { this.clientAccounts = accounts; this.updateChart(); }) 设置为0的原因

答案 1 :(得分:1)

您的组件需要在渲染之前具有数据。您可以使用resolve,这是Angular提供的内置功能,可以处理您所描述的用例。

也请看here。可能是教程形式中的有用资源。

答案 2 :(得分:1)

public function send_mail() {
            $this->load->library('email');
            $name = $this->input->post("name");
            $email = $this->input->post("email");
            $config['protocol'] = 'smtp';
            $config['smtp_host'] = 'mail.*******.com';
            $config['smtp_port'] = '465';
            $config['smtp_timeout'] = '7';
            $config['smtp_user'] = 'info@*******.com';
            $config['smtp_pass'] = '*********';
            $config['charset'] = 'utf-8';
            $config['newline'] = '\r\n';
            $config['mailtype'] = 'text'; // or html
            $config['validation'] = TRUE;

            $this->email->initialize($config);
            $this->email->from('info@*******.com', 'Hima');
            $this->email->to($email);
            $this->email->subject('email verification');
            $this->email->message('Verification Code');
            $send = $this->email->send();
            if ($send) {
                $this->session->set_flashdata('msg','success');
            } else {
                echo $error = $this->email->print_debugger();
            }
        }

ngOnChanges的自变量值为每个ngOnChanges(changes: SimpleChanges) { if (changes['accountBalanceStatus'] || changes['chart']) { this.renderChart(); } } 的{​​{3}}类型  道具:

Input()

您应按class SimpleChange { constructor(previousValue: any, currentValue: any, firstChange: boolean) previousValue: any currentValue: any firstChange: boolean isFirstChange(): boolean } previousValue检查数据。 像这样:

currentValue

SimpleChanges

答案 3 :(得分:0)

您需要与父级的子级组件交互,以使用输入绑定。

引用:

https://angular.io/guide/component-interaction#pass-data-from-parent-to-child-with-input-binding

答案 4 :(得分:0)

我的问题已解决,我希望与大家分享解决方案,以防将来有人需要。正如阿米特·奇加达尼(Amit Chigadani)建议的那样(在评论中),在我的订阅块中调用我的图表更新功能是有效的。

getAccountsOfClientId(clientID: string): void {
    this.clientAccountService.getAccountsOfClientId(this.client.id)
      .subscribe(accounts => {
        this.clientAccounts = accounts;
        this.updateChart();
        this.updateBarChart();
      });
}