0

BEFORE tagging it as a duplicate, note that None of these worked for me: question 1 / question 2 / question 3

So, after getting a reply to a previous question I started using rxjs following everything stated on the guide I was provided with

However, I soon got stuck again for the last couple of hours because of the subscribe method not being triggered.

I've coded my service like:

import { Injectable } from '@angular/core';
import { Subject }    from 'rxjs/Subject';
import {FormDTO} from "../dto/formDTO.model";

@Injectable()
export class ApiDosageDialogFormCommunicationService {

    private formDTO = new Subject<FormDTO>();

    //Observable string streams
    formDTOpulling$ = this.formDTO.asObservable();

    //Service message commands
    formDTOpushing(formDTO: FormDTO) {
        this.formDTO.next(formDTO);
    }

Sending it from the parent to the child like:

this.apiDosageDialogFormCommunicationService.formDTOpushing(this.formDTO)

It works fine up until here, the service receives the DTO and I can read its values, but when I try to use it in the modal service, the subscribe method does nothing at all, this is the component in which I'm trying to fetch the DTO:

  import {Component, OnDestroy, OnInit} from "@angular/core";
import {JhiEventManager} from "ng-jhipster";
import {NgbActiveModal} from "@ng-bootstrap/ng-bootstrap";
import {Response} from "@angular/http";
import {ActivatedRoute} from "@angular/router";
import {Observable} from "rxjs/Rx";
import {FormService} from "./form.service";
import {FormDTO} from "../dto/formDTO.model";
import {ApiDosageDialogFirstStepPopupService} from "./apiDosage-dialog-first-step-popup.service";
import {ApiDosageDialogFormCommunicationService} from "./apiDosageDialogFormCommunication.service";
import {Subscription} from "rxjs/Subscription";

@Component({
    selector: 'jhi-apiDosage-dialog-first-step',
    templateUrl: './apiDosage-dialog-first-step.component.html'
})

export class ApiDosageDialogFirstStepComponent implements OnInit {

    formDTO: FormDTO;
    isSaving: boolean;

    constructor(
        public activeModal: NgbActiveModal,
        private formService: FormService,
        private apiDosageDialogFormCommunicationService: ApiDosageDialogFormCommunicationService,
        private eventManager: JhiEventManager
    ) {
    }

    ngOnInit() {
        this.isSaving = false;
    }

    clear() {
        this.activeModal.dismiss('cancel');
    }

    save() {
        this.isSaving = true;
        if (this.formDTO.id !== undefined) {
            this.subscribeToSaveResponse(
                this.formService.save(this.formDTO));
        } else {
            this.subscribeToSaveResponse(
                this.formService.save(this.formDTO));
        }
    }

    private subscribeToSaveResponse(result: Observable<FormDTO>) {
        result.subscribe((res: FormDTO) =>
            this.onSaveSuccess(res), (res: Response) => this.onSaveError());
    }

    private onSaveSuccess(result: FormDTO) {
        this.eventManager.broadcast({ name: 'journalListModification', content: 'OK'});
        this.isSaving = false;
        this.activeModal.dismiss(result);
    }

    private onSaveError() {
        this.isSaving = false;
    }
}

@Component({
    selector: 'jhi-apiDosage-first-step-popup',
    template: ''
})
export class ApiDosageDialogFirstStepPopupComponent implements OnInit, OnDestroy {

    routeSub: any;
    formDTO: any;
    subscription: Subscription;

    constructor(
        private route: ActivatedRoute,
        private apiDosagePopupService: ApiDosageDialogFirstStepPopupService,
        private apiDosageDialogFormCommunicationService: ApiDosageDialogFormCommunicationService
    ) {

    }

    ngOnInit() {
        this.subscription = this.apiDosageDialogFormCommunicationService.formDTOpulling$.subscribe(
            formDTO => {
                 console.log('In the component' +this.formDTO.sample.id),
                 this.formDTO = formDTO;
             },
             error => {
                 alert('Made it all the way here: '+this.formDTO);
             });
        this.routeSub = this.route.params.subscribe((params) => {
            this.apiDosagePopupService
                    .open(ApiDosageDialogFirstStepComponent as Component, this.formDTO);
        });
    }

    ngOnDestroy() {
        this.routeSub.unsubscribe();
    }
}

Please note that in the previous class, I've tried adding the subscribe method in both onInit and in the constructors.

The previous class calls open method of the modal, being this the source code for the modal's service:

import { Injectable, Component } from '@angular/core';
import { Router } from '@angular/router';
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import {FormService} from "./form.service";
import {FormDTO} from "../dto/formDTO.model";
import {Observable} from "rxjs/Observable";

@Injectable()
export class ApiDosageDialogFirstStepPopupService {
    private ngbModalRef: NgbModalRef;

    constructor(
        private modalService: NgbModal,
        private router: Router,
        private formService: FormService

    ) {
        this.ngbModalRef = null;
    }

    open(component: Component, formDTO?: Observable<any> | any): Promise<NgbModalRef> {
        return new Promise<NgbModalRef>((resolve, reject) => {
            const isOpen = this.ngbModalRef !== null;
            if (isOpen) {
                resolve(this.ngbModalRef);
            }
            if (formDTO) {
                  // this.formService.find(id).subscribe((journal) => {
                  //     this.ngbModalRef = this.journalModalRef(component, journal);
                  //     resolve(this.ngbModalRef);
                  // });
                setTimeout(() => {
                    this.ngbModalRef = this.apiDosageFirstStepModalRef(component, formDTO);
                         resolve(this.ngbModalRef);
                         console.log(formDTO);
                }, 0);
                } else {
                 //setTimeout used as a workaround for getting ExpressionChangedAfterItHasBeenCheckedError
                 setTimeout(() => {
                     this.ngbModalRef = this.apiDosageFirstStepModalRef(component, new Observable<any>());
                     resolve(this.ngbModalRef);
                 }, 0);
                alert('no form');
            }
        });
    }

    apiDosageFirstStepModalRef(component: Component, formDTO: Observable<any>): NgbModalRef {
        const modalRef = this.modalService.open(component, { size: 'lg', backdrop: 'static'});
        modalRef.componentInstance.formDTO = formDTO;
        modalRef.result.then((result) => {
            this.router.navigate([{ outlets: { popup: null }}], { replaceUrl: true, queryParamsHandling: 'merge' });
            this.ngbModalRef = null;
        }, (reason) => {
            this.router.navigate([{ outlets: { popup: null }}], { replaceUrl: true, queryParamsHandling: 'merge' });
            this.ngbModalRef = null;
        });
        return modalRef;
    }
}
Steven
  • 1,236
  • 1
  • 13
  • 37
  • Do your components subscribe to `formDTOpulling$` after you call the `formDTOpushing` method? If so, they will not receive the subject emission. They will only receive the emissions after they subscribe. If this is the case, then you can use something like a BehaviorSubject or ReplaySubject to receive the last value emitted when you subscribe. – LLai Jan 18 '18 at 18:57
  • I believe so, wouldn't it be this line: `this.subscription = this.apiDosageDialogFormCommunicationService.formDTOpulling$.subscribe( formDTO => { console.log('In the component' +this.formDTO.sample.id), this.formDTO = formDTO; }, error => { alert('Made it all the way here: '+this.formDTO); });`? – Steven Jan 18 '18 at 19:00
  • Ok, I caught your comment too early, so you're suggesting that I call the child as soon as I use the push method? – Steven Jan 18 '18 at 19:01
  • I'm saying that the order of what is being called matters. So what might be happening is `formDTOpushing` gets called (and no components are subscribed to the subject). Now the components subscribe. They do not receive the value from the subject because `formDTOpushing` occurred before they subscribed. – LLai Jan 18 '18 at 19:07
  • Is there a working example of something similar? I'm struggling to see how this would work, if I try to subscribe to the subject before I push from the parent, I will get an undefined, wouldn't I? – Steven Jan 18 '18 at 19:11
  • The [rxjs](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/subjects.md) docs have some pretty good examples of how the different types of Subjects work. With a plain Subject (like in your case), you do not receive a value when you subscribe. You only receive values that get pushed after you subscribe. This is contrary to something like BehaviorSubjects or ReplaySubjects, where you receive the previous value(s) when you subscribe. – LLai Jan 18 '18 at 19:16
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/163439/discussion-between-llai-and-steven). – LLai Jan 18 '18 at 19:16

1 Answers1

2

What is happening is formDTOpushing is being called before the components subscribe. This means the components will not receive that value. Components that subscribe to a Subject only receive values that are emitted after they subscribe. (They do not receive a value when they initially subscribe)

To have the the components receive previous emitted value(s) on subscribe, use a BehaviorSubject or ReplaySubject instead

LLai
  • 13,128
  • 3
  • 41
  • 45
  • Genius! I changed `private formDTO = new Subject();` for `private formDTO = new BehaviorSubject(this.formDTO1);` And it works just fine – Steven Jan 18 '18 at 19:49