Fakhry
Fakhry

Reputation: 49

How to chain http requests in two components in two services

I have PropertyComponent(Parent) and FeaturesComponent(Child), each one has its own service. On receiving the response from parent I send the selected features to the child and on receiving the child response I change the "isChecked= true" according to the selected features from parent.

The problem that sometimes the child response is received before the parent response so all features will be "isChecked= false".

I need to delay the child request till the parent response is received.

Property model:

export class Property { 
    id: string;
    numberOrName: string;
    selectedFeatures: string[];
}

Property service:

export class PropertService{
getPropert(id: number){
this.http.get(baseUrl + id).map((response: Response) =>   <Property[]>response.json().data);
}

Property component:

export class EditPropertyComponent implements OnInit{
@ViewChild('pf') propertyFeatures: PropertyFeaturesComponent;
    editProperty: Property;
    id: string;
    constructor(private propertyService: PropertyService){}

        ngOnInit() {
            this.route.params
                .map(params => params['id'])
                .do(id => this.id = id)
                .subscribe(id => {
                        this.getProperty(id);
                });
        }

        getProperty() {
            this.propertyService.getOne(this.id).subscribe(
            prop => {
                this.editProperty = prop;
                this.selectedFeatures = prop.selectedFeatures;
                this.propertyFeatures.selectedFeatures = this.selectedFeatures;
            }
        );
    }
    }

Feature service:

export class FeatureService {
    constructor(private http: Http) { }
    get() {

        return this.http.get(this.baseURL)
            .map((response: Response) => {console.log(response.json().data); return <Feature[]>response.json().data;});
    }
}

property feature component (child):

export class PropertyFeaturesComponent implements OnInit {
    @Input() selectedFeatures: string[];

    features: Feature[];

    constructor(private featureService: FeatureService) { }
    ngOnInit() {
        this.getFeatures();
    }

    getFeatures() {
        this.featureService.get()
            .subscribe((res) => {
                this.features = res;
                //If the parent response received before this subscribe then selectedFeatures will contain values otherwise the next line will be useless. this.selectedFeatures is an @Input() and I send its value from parent
                this.features.filter(f => this.selectedFeatures.find(s => s == f.id)).forEach(f => f.isChecked = true);
            });
    }
}

I wish the problem is clear.

Upvotes: 1

Views: 181

Answers (1)

Steve G
Steve G

Reputation: 13397

It sounds like you will need to initiate serial requests to ensure timing between parent and child (which I believe that you already know).

There are a few ways to accomplish this, but this is the method that I prefer for simplicity.

Overview (TLDR)

ParentComponent::ngOnInit() fires making your first request. On success/failure ParentComponent "alerts" the ChildComponent through @Input() data: any; // Or whatever type. ChildComponent::ngOnChanges() then initiates the second request on change of its data property.

Code Sample

Using your code as a reference, I've tried to create this example, but it may require a little more refactoring that you'd like to perform.

property.component.ts:

import { Component, OnInit } from '@angular/core';
import { ActiveRoute } from '@angular/router';
import { PropertyService } from './property.service';
import { Property } from './property.model';

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

  editProperty: Property;
  id: string;

  constructor(
    private route: ActiveRoute,
    private propertyService: PropertyService) { }

  ngOnInit(): void {
    this.route.params
      .map(params => params['id'])
        .do(id => this.id = id)
        .subscribe(id => {
          this.getProperty(id);
        });
  }

  getProperty(): void {
    this.propertyService.getOne(this.id).subscribe(
      prop => {
        // Looks like prop already had features, so I removed it for 
        // brevity, but you can add it back if needed
        this.editProperty = prop;
      }
    );
}

property.component.html:

<div> 
  ... <!-- Other markup -->

  <feature-component 
    *ngIf="editProperty" 
    [propertyData]="editProperty">
  </feature-component>
</div>

feature.component.ts:

import { Component, OnChanges, Input } from '@angular/core';
import { FeatureService } from './feature.service';
import { Property } from './property.model';

@Component({
  selector: 'feature-component',
  templateUrl: './feature.component.html',
  styleUrls: ['./feature.component.css']
})
export class FeatureComponent implements OnChanges {

  @Input() 
  private propertyData: Property; // Looks like editProperty already has features, so I removed it for brevity

  constructor(private featureService: FeatureService) { }

  ngOnChanges(): void {

    // Fires when propertyData changes

    if (this.propertyData) {
      // Whatever you need to do with this.propertyData.selectedFeatures

      this.featureService.get(...).subscribe((...) => {
        // Do work. Removed for brevity

        // If you need to alert the parent of a change in features for some reason, you 
        // can use an EventEmitter, but be careful of an infinite loop.
      });
    }
  }
}

Note: Untested code for illustration.

I hope this helps!

Upvotes: 1

Related Questions