从父组件中的 ajax 调用接收数据后如何更新子组件 [英] How to update child components after receiving data from an ajax call in parent

查看:17
本文介绍了从父组件中的 ajax 调用接收数据后如何更新子组件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的应用程序结构如下,我的问题是如何在接收初始或未来数据时更新子组件视图,假设我只有一个具有 OnDataUpdate 事件的服务,所有子组件都接收相同的服务实例既然它已经在 App 模块提供者部分声明,另一方面,我已经尝试了所有这些方法 &没有用:

my app structure is as below, my question is how to update children components view on receiving initial or future data, imagine I have only one service which has an event OnDataUpdate, all the child components are receiving the same instance of the service since it has declared in App module providers section, on the other hand, I have tried all these ways & did not work:

  1. ApplicationRef.tick()
  2. ChangeDetectionRef.markForCheck()
  3. ChangeDetectionStrategy
  4. 使用 OnDataRecieved 事件在组件之间共享服务,就像这样

@Injectable()
export class ApiService {

  public OnDataRecieved: EventEmitter<Model>  = new EventEmitter<Model>();

  constructor(private http: HttpClient, private ngZone: NgZone) {
  }

  public getDataAsync(): Observable<Model> {
      return this.http
        .get<Model>('url')
        .pipe(catchError(er => throwError(er)));
    }
}

在应用根组件中,这就像下面的代码

and in App root component this is like below code

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
  changeDetection: ChangeDetectionStrategy.Default
})
export class AppComponent implements DoCheck {

  model: BehaviorSubject<Model> = new BehaviorSubject<Model>(new Model()); //with default values
  subModel: BehaviorSubject<SubModel>; 


  constructor(private apiService: ApiService,
    private zone: NgZone) {

    this.apiService.getDashboard().subscribe((data) => {
      this.zone.run(() => {
          this.apiService.OnDataReceived.emit(data);
          this.model = new BehaviorSubject<Model>(data);
      });
    });

    this.model.subscribe((mdl) => {
      this.subModel = new BehaviorSubject<SubModel>(mdl.subModel));
    });
  }

  ngDoCheck() {
  }
}

想象一下,当数据加载或更改时,模型嵌套并通过子组件传播,结构可以是这样的

imagine the model is nested and propagated through the child components as data is loaded or changed, the structure can be like this

 __ AppRootComponent
|_____ Component1
|_________SubCompoent1-1
|_________SubCompoent1-2
|_____ Component2
|_________SubCompoent2-1
|____________SubCompoent2-1-1

我在 ngDoCheck 中收到数据更改,不需要触发检测更改,但 UI 和子组件没有更新!

I receive the data changes in ngDoCheck, no need to trigger the detect changes, but the UI and child components does not get updated!

推荐答案

我意识到如何解决这个问题,那个组件的结构是分层的,我通过@Input() 传递了每个组件的模型,问题是初始请求是异步的,组件在接收到真正的父对象之前渲染,从服务器接收到父对象后,传递的 Input 对象没有对象引用,因此它们不会得到更改.

i realized how to solve that issue, the structure of that components is hierarchical and I passed the model of each component through @Input(), the problem is the initial request is async and the components are rendered before receiving the real parent object and after receiving the parent object from server there is no object reference for the passed Input objects, so they will not get the changes.

那么,我们该如何解决这个问题呢?简单的!删除所有输入并使用事件驱动编程如何?为每个对象创建一个事件或为所有其他对象所依赖的父(根)对象创建一个事件,在全局服务中共享事件,一旦收到根对象就触发/发出事件,并在子组件中订阅该事件.让我在下面向您展示一个简单的片段:

so, how can we solve the issue? simple! remove all inputs and use event-driven programming How? create an event for each object or one event for the parent (root) object which all other objects depend on, share event in global service, trigger/emit event once you receive the root object, and subscribe to that event in child components. let me show you a simple snippet at below:

import { HttpClient, HttpParams, HttpErrorResponse } from '@angular/common/http';
import { Injectable, EventEmitter } from '@angular/core';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

import { RootDto } from 'src/app/model/root.dto.model';

@Injectable()
export class CoreApiService {

  public onDataReceived: EventEmitter<RootDto> = new EventEmitter<RootDto>();

  constructor(private http: HttpClient) {
  }

  public getRootObject(objectId: number): Observable<RootDto> {
     // const _params = new HttpParams().set('objectId', objectId);
      return this.http
        .get<RootDto>(`${Constants.ApiUrl}/root/${objectId}`)
        .pipe(catchError((err: HttpErrorResponse) => {
          return throwError(err);
        }));
    }
}

根辅音如下

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

import { CoreApiService } from './core/services/core-api.service';
import { RootDto } from 'src/app/model/root.dto.model';

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

  constructor(private apiService: CoreApiService) {

  }

  ngOnInit() {
    this.apiService.getRootObject().subscribe((data: RootDto) => {
			// todo: do something here
          this.apiService.onDataReceived.emit(data);
        },
        (err: HttpErrorResponse) => {
          if (err.status === 401 || err.status === 403) {
            // not authorized
          }else {
          // todo: error happened!
		  }
        }
      );
  }
}

子组件如下

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

import { CoreApiService } from '../core/services/core-api.service';
import { RootDto } from 'src/app/model/root.dto.model';
import { ChildDto } from '../model/child.dto.model';

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

  dto: ChildDto;
  isLoaded = false;

  constructor(private apiService: CoreApiService, private zone: NgZone) {
    this.apiService.onDataReceived.subscribe((rootDto: RootDto) => {
      this.zone.run(() => {
        this.dto = Utils.ObjectFactory.Create(rootDto.firstChildDto); // to make sure that we will have a new reference (so that change detction will be triggered) i use object instantiation
		// NOTICE:
		// for arrays don't simply assign or push new item to the array, because the reference is not changed the change detection is not triggered
		// if the array size is small before assigning new value, you can simply empty (myArray = [];) the array otherwise don't do that
        this.isLoaded = true;
      });
    });
  }

  ngOnInit() {
  }

  // the rest of logic
}

您可以对所有其他组件执行相同操作,甚至可以在共享服务中创建更多事件并根据需要触发它

you can do the same for all other components and even you can create more events in share service and trigger it as you wish

这篇关于从父组件中的 ajax 调用接收数据后如何更新子组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆