Search code examples
angularngrx-storengrx-effects

Problem with multiple request in subscribe


I am invoking a method on my component that loads the address information through the postalcode entered in the input. First I am invoking the method that loads the information into the getAddress$ variable, then subscribe to it to get the data and assign it to form inputs. On the first page load, it only executes one call in the api, but as I inform another postalcode, my subscribe will add one more return, making more than one call to the api. What I want to do is for each postalcode entered, give me only 1 result for each postal code. Below is my code, the method is firing on the input blur event. I have already implemented all the unsuccessful solutions contained in this article https://blog.angularindepth.com/the-best-way-to-unsubscribe-rxjs-observable-in-the-angular-applications-d8f9aa42f6a0 Could you help me with this problem please? What am I doing wrong?

Thanks!

// class CommonEffect

@Injectable()
export class CommonEffect {
    constructor(private actions$: Actions,
        private authApi: CommonService) {
    }
    @Effect()
    getAddress$: Observable<Action> = this.actions$
        .pipe(
            ofType(actions.ActionTypes.GET_ADDRESS),
            map((action: actions.GetAddress) => action.payload),
            switchMap((state) => {
                return this.authApi.getAddress(state)
                    .pipe(
                        map((address) => new actions.GetAddressSuccess(address)),
                        catchError(error => of(new actions.GetAddressFail(error)))
                    );
            })
        );
}



// function reducer

export function reducer(state = initialState, { type, payload }: any): CommonState {

    if (!type) {

        return state;
    }
    switch (type) {
        case actions.ActionTypes.GET_ADDRESS:
            {

                return Object.assign({}, state, {
                    getAddressLoading: true,
                    getAddressLoaded: false,
                    getAddressFailed: false,
                });
            }

        case actions.ActionTypes.GET_ADDRESS_SUCCESS: {
            const tempAddress = new SearchAddressModel(payload.data);
            return Object.assign({}, state, {
                address: tempAddress,
                getAddressLoading: false,
                getAddressLoaded: true,
                getAddressFailed: false,
            });
        }
        case actions.ActionTypes.GET_ADDRESS_FAIL:
            {
                return Object.assign({}, state, {
                    getAddressLoading: false,
                    getAddressLoaded: true,
                    getAddressFailed: true,
                });
            }

        default: {
            return state;
        }
    }
}

// class CommonSandbox

@Injectable()
export class CommonSandbox {

    /* get address*/
    public getAddress$ = this.appState$.select(getAddress);
    public addressLoading$ = this.appState$.select(addressLoading);
    public addressLoaded$ = this.appState$.select(addressLoaded);
    public addressFailed$ = this.appState$.select(addressFailed);

    constructor(private router: Router,
        protected appState$: Store<store.AppState>,
    ) {
    }
    public getAddress(params) : void {
        this.appState$.dispatch(new commonAction.GetAddress(params));
    }

}

// class component

export class AddaddressesComponent implements OnInit, OnDestroy {

    addAddressForm: FormGroup;
    addressId: any;
    openAddress = false;
    private subscriptions: Array<Subscription> = [];

    constructor(private route: ActivatedRoute, private router: Router, public formBuilder: FormBuilder, public snackBar: MatSnackBar, public commonSandbox: CommonSandbox, public accountSandbox: AccountSandbox) {
    }

    ngOnInit() {
        this.addressId = this.route.snapshot.paramMap.get('id');
        this.addAddressForm = this.formBuilder.group({
            'firstName': ['', Validators.required],
            'lastName': ['', Validators.required],
            'address': ['', Validators.required],
            'phoneNumber': '',
            'phoneMobileNumber': ['', Validators.required],
            'complement': '',
            'reference': '',
            'addresstype': '',
            'city': ['', Validators.required],
            'zone': ['', Validators.required],
            'state': ['', Validators.required],
            'postalcode': ['', Validators.required]
        });
        this.addAddressForm.patchValue({ addresstype: '1', tc: true });

    }

    // method (blur) search address for postalcode
    public getSeacrhAddress(value: any) {
        if (value) {

            // Here I call the api that returns the address according to the postalcode entered, below I retrieve the value through subscribe.
            this.commonSandbox.getAddress(value.replace(/[^\d]+/g, ''));

            // the subscribe address parameter in the first pass on the first page load is undefined, as I inform another postalcode it always has the previous value
            this.subscriptions.push(this.commonSandbox.getAddress$.subscribe(address => {
               //With the breakpoint here, each postalcode you enter will increment one more pass instead of just once.
                if (address) {
                    this.addAddressForm.controls['address'].setValue(address.logradouro);
                    this.addAddressForm.controls['city'].setValue(address.localidade);
                    this.addAddressForm.controls['zone'].setValue(address.bairro);
                    this.addAddressForm.controls['state'].setValue(address.uf);
                    this.openAddress = true;
                }
            }));
        }
    }

    // destroy the subscribed events while page destroy
    ngOnDestroy() {
        this.subscriptions.forEach(each => {
            each.unsubscribe();
        });
    }
}

Solution

  • You don't need to add to this.subscriptions on every blur event. Instead you can subscribe to it once in ngOnInit.

    ngOnInit() {
        this.addressId = this.route.snapshot.paramMap.get('id');
        this.addAddressForm = this.formBuilder.group({
          'firstName': ['', Validators.required],
          'lastName': ['', Validators.required],
          'address': ['', Validators.required],
          'phoneNumber': '',
          'phoneMobileNumber': ['', Validators.required],
          'complement': '',
          'reference': '',
          'addresstype': '',
          'city': ['', Validators.required],
          'zone': ['', Validators.required],
          'state': ['', Validators.required],
          'postalcode': ['', Validators.required]
        });
        this.addAddressForm.patchValue({ addresstype: '1', tc: true });
    
    // the subscribe address parameter in the first pass on the first page load is undefined, as I inform another postalcode it always has the previous value
        this.subscriptions.push(this.commonSandbox.getAddress$.subscribe(address => {
          //With the breakpoint here, each postalcode you enter will increment one more pass instead of just once.
          if (address) {
            this.addAddressForm.controls['address'].setValue(address.logradouro);
            this.addAddressForm.controls['city'].setValue(address.localidade);
            this.addAddressForm.controls['zone'].setValue(address.bairro);
            this.addAddressForm.controls['state'].setValue(address.uf);
            this.openAddress = true;
          }
        }));
    
      }
    
      // method (blur) search address for postalcode
      public getSeacrhAddress(value: any) {
        if (value) {
    
          // Here I call the api that returns the address according to the postalcode entered, below I retrieve the value through subscribe.
          this.commonSandbox.getAddress(value.replace(/[^\d]+/g, ''));
        }
      }