1
\$\begingroup\$

I have googled and read many tutorials about fetching data and posting data to Rest API. But i have never seen a tutorial with how to handle the getting of new data after a POST or an PUT in a best practice way. So i experimented a bit and codes this. It is working but I don't know if it is the correct way of doing this.

In customerservice I have a subject that components can subscribe to. When I'm getting customers from the API I'm publishing the result into the subject. And in the component I'm subscribing to a customer subject.

In a POST to the API I'm triggering a GetCustomer() to fetch all customers again.

CustomerService

import { Injectable } from '@angular/core';
import { LoggerService } from 'src/app/core/services/logger.service';
import { Observable, Subject } from 'rxjs';
import { Customer } from 'src/app/shared/models/customer.model';
import { HttpClient } from '@angular/common/http';
import { tap, map } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class CustomersService {
  customers = new Subject<Customer[]>();

  constructor(private http: HttpClient, private logger: LoggerService) { }

  getCustomers(): void {
    this.http.get<Customer[]>('/subscribers/customers').pipe(map(res => {
      this.logger.info('Getting customers', res);
      this.customers.next(res);
    })).subscribe();
  }

  addCustomer(customer: Customer): void {
    this.http.post<Customer[]>('/subscribers/customers', customer).pipe(map(res => {
      this.logger.info('Adding new customer', res);
      this.getCustomers();
    })).subscribe();
  }
}

CustomerComponent

import { Component, OnInit } from '@angular/core';
import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
import { CustomerDialogComponent } from '../../components/customer-dialog/customer-dialog.component';
import { CustomersService } from '../../services/customers.service';
import { Observable } from 'rxjs';
import { Customer } from 'src/app/shared/models/customer.model';
import { LoadingWrapper } from 'src/app/shared/helpers/LoadingWrapper';

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

  bsModalRef: BsModalRef;
  customers: LoadingWrapper<Customer[]>;

  constructor(private modalService: BsModalService, private customersService: CustomersService) { }

  ngOnInit() {
    this.customers = new LoadingWrapper(this.customersService.customers);
    this.customersService.getCustomers();
  }

  open(): void {
    this.bsModalRef = this.modalService.show(CustomerDialogComponent, { backdrop: false, class: 'right-modal' });
  }
}

And finally my modals that's POST a new customer:

import { Component, OnInit } from '@angular/core';
import { FormGroup, Validators } from '@angular/forms';
import { FormInputBase } from 'src/app/shared/models/form-input-base.model';
import { LoggerService } from 'src/app/core/services/logger.service';
import { FormInputText } from 'src/app/shared/models/form-input-text.model';
import { FormService } from 'src/app/core/services/form.service';
import { BsModalService } from 'ngx-bootstrap';
import { CustomersService } from '../../services/customers.service';

@Component({
  selector: 'app-add-customer',
  templateUrl: './add-customer.component.html',
  styleUrls: ['./add-customer.component.scss']
})
export class AddCustomerComponent implements OnInit {
  addCustomerForm: FormGroup;
  addCustomerInputs: FormInputBase<any>[];

  constructor(private logger: LoggerService, private formService: FormService,
    private modalService: BsModalService, private customerService: CustomersService) { }

  ngOnInit() {
    this.addCustomerInputs = this.getCustomerInputs();
    this.addCustomerForm = this.formService.toFormGroup(this.addCustomerInputs);
  }

  getCustomerInputs(): FormInputBase<any>[] {
    const inputs: FormInputBase<any>[] = [
      new FormInputText({
        key: 'name',
        label: 'Name',
        placeholder: 'Customer name',
        order: 1,
        validation: [Validators.required, Validators.maxLength(200)]
      }),
      new FormInputText({
        key: 'ref',
        label: 'Ref',
        placeholder: 'External reference',
        order: 2
      })
    ];
    return inputs.sort((a, b) => a.order - b.order);
  }

  add(): void {
    this.customerService.addCustomer(this.addCustomerForm.value);
  }

  close(): void {
    this.modalService.hide(1);
  }
}

EDIT: LoadingWrapper

import { Subject, Observable, of } from 'rxjs';
import { shareReplay, catchError } from 'rxjs/operators';
import { LoggerService } from 'src/app/core/services/logger.service';

export class LoadingWrapper<T> {
  private readonly _errorLoading$ = new Subject<boolean>();
  readonly errorLoading$: Observable<boolean> = this._errorLoading$.pipe(
    shareReplay(1)
  );
  readonly data$: Observable<{} | T>;

  constructor(data: Observable<T>) {
    this.data$ = data.pipe(
      shareReplay(1),
      catchError(error => {
        this._errorLoading$.next(true);
        return of();
      })
    );
  }
}
\$\endgroup\$
  • \$\begingroup\$ What is LoadingWrapper? The way you're using observables in your service seems like an antipattern, but without seeing LoadingWrapper I can't tell what exactly is happening. \$\endgroup\$ – elclanrs Mar 17 at 20:37
  • \$\begingroup\$ @elclanrs Loading wrapper is just a generic way to handle errors and showing the loading text. \$\endgroup\$ – Tan Mar 17 at 20:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.