1

I have a detail component and child components overview and contact, I have used a shared service to share an array ImageMap which is updated by the detail after retrieving from a service on its initialization and subscribed and accessed by the children on their respective init methods. When i navigate to child components manually typing the address in address bar the the snippet in the ngOnInit of child component to set the array value from shared service gets loaded, whereas while navigating by router.navigate on click of buttons, apart from that portion in NgOnInit everything else gets loaded. Please help me on this. Where have i gone wrong ?

//Account detail component.html

<h3>You selected brand {{item_name}}</h3>
<p>
    <button (click)="showOver()" class="btn btn-primary">Overview</button>
    <button (click)="showCon()" class="btn btn-info">Contact</button>
</p>
<router-outlet></router-outlet>
<button (click)="gotoAccounts()" class="btn btn-warning">Back</button>

//Datashare Service.ts

import { Injectable } from '@angular/core';
import { Subject,BehaviorSubject } from 'rxjs/Rx';

@Injectable({
    providedIn: 'root'
})
export class DatashareService {

  private dataObs$ =  new Subject();



     getData() {
        return this.dataObs$;
                }

updateData(data) {
    this.dataObs$.next(data);
                  }

 constructor() { }
}

//Account Detail Component.ts

import { Component, OnInit} from '@angular/core';
import { ActivatedRoute,Router,ParamMap } from '@angular/router';
import { ImageFetchService } from '../image-fetch.service';
import { DatashareService } from '../datashare.service';

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

public item_id;
public item_name;
public imageMap;
public errorMsg;
constructor(private route: ActivatedRoute,private router:Router,private 
imageService: ImageFetchService,
private dataService: DatashareService) { }

ngOnInit() {

//let id = parseInt(this.route.snapshot.paramMap.get('id'));
//this.item_id=id;

this.route.paramMap.subscribe((params: ParamMap)=>
{
  let id = parseInt(params.get('id'));
  this.item_id=id;
  let sel_name = params.get('name');
  this.item_name=sel_name;
  console.log(this.item_id,this.item_name);
}  )

 this.imageService.getImages().subscribe(data =>{ this.imageMap=data;

 this.dataService.updateData(this.imageMap);},
                                              error => this.errorMsg=error);




 }



  showOver()
  {
               let sel_name= this.item_name?this.item_name:null;

                this.router.navigate(['overview',{"name":sel_name}],
                {relativeTo:this.route})


   }

   showCon()
   {
       let sel_name= this.item_name?this.item_name:null;

       this.router.navigate(['contact',{"name":sel_name}],
       {relativeTo:this.route})

   }
  }

//Account Overview Component.ts

import { Component, OnInit,Input,OnDestroy,NgZone } from '@angular/core';
import {NgbCarouselConfig} from '@ng-bootstrap/ng-bootstrap';
import {map} from 'rxjs/operators';
import {HttpClient} from '@angular/common/http';
import { ActivatedRoute,Router,ParamMap } from '@angular/router';
import { DatashareService } from '../datashare.service';
import { Subscription } from 'rxjs/Subscription';


@Component({
  selector: 'app-account-overview',
  templateUrl: './account-overview.component.html',
  styleUrls: ['./account-overview.component.css'],
  providers: [NgbCarouselConfig]
})
export class AccountOverviewComponent implements OnInit,OnDestroy {
private subscription: Subscription = new Subscription();
public imageArray;
 public imageMap;
 public errorMsg;
 public selected_name;
  constructor(config: NgbCarouselConfig, private _http: HttpClient
  ,private route:ActivatedRoute,private dataService: DatashareService
  ,private zone:NgZone) {
    // customize default values of carousels used by this component tree
    config.interval = 2000;
    config.keyboard = false;
    config.pauseOnHover = false;
  }

  ngOnInit() {

   this.route.paramMap.subscribe((params: ParamMap)=>
        {
            let name =params.get('name');
            this.selected_name=name;
            console.log(this.selected_name);
        })

        this.subscription.add(this.dataService.getData().subscribe(data => { //<== added this

            this.zone.run(()=>{
                this.imageArray = data;
            })

        }))







  }

  ngOnDestroy() {
        // unsubscribe to ensure no memory leaks
        this.subscription.unsubscribe();
    }




}
Mithil Mohan
  • 223
  • 2
  • 11

2 Answers2

1

Using BehaviourSubject caused the subscribe method in child component fire twice, first with an initial value {} and then with the last updated value. So I changed it to ReplaySubject which doesn't need an initial value.

import { ReplaySubject } from 'rxjs/Rx';

export class DatashareService {

    private dataObs$ = new ReplaySubject<any>(1);

    getData() {
        return this.dataObs$.asObservable();
    }

    updateData(data) {
        this.dataObs$.next(data);
    }

    constructor() { }
}
Mithil Mohan
  • 223
  • 2
  • 11
0

Try to return dataObs$ as Observable in your DatashareService.ts and replace your Subject by a BehaviorSubject.

import { BehaviorSubject } from 'rxjs/Rx';

export class DatashareService {

    private dataObs$ = new BehaviorSubject<any>({});

    getData() {
        return this.dataObs$.asObservable();
    }

    updateData(data) {
        this.dataObs$.next(data);
    }

    constructor() { }
}
  • Tried that, the result is still the same – Mithil Mohan Aug 06 '18 at 06:08
  • No error in console at all, when i navigate through button click it doesn't execute, when i refresh it by hitting enter in the address bar it does – Mithil Mohan Aug 06 '18 at 06:15
  • Does your code actually enter the ngOnInit-method? Can you check this by putting console.log() in the topmost line of code inside ngOnInit? –  Aug 06 '18 at 06:24
  • Yes it does, everything except that portion is executed, the parameter reading and console statements do work, only this portion doesn't get rendered – Mithil Mohan Aug 06 '18 at 06:25
  • Okay, next question. Does `console.log()` print anything inside `this.subscription.add(this.dataService.getData().subscribe(data => {...` ? Can you access `data` there? –  Aug 06 '18 at 06:28
  • No that is exactly what my issue is, it doesn't enter that snippet or simply that portion alone isn't executed – Mithil Mohan Aug 06 '18 at 06:34
  • That's odd. Your code looks ok. One last idea. Replace your Subject in the service with a BehaviorSubject. –  Aug 06 '18 at 06:43
  • For clarity on BehaviorSubject refer : https://stackoverflow.com/questions/39494058/behaviorsubject-vs-observable – Mithil Mohan Aug 06 '18 at 06:51
  • You're welcome. I updated my answer. Please, don't forget to mark it as solution. Thanks in advance. –  Aug 06 '18 at 06:52
  • Using a behavior subject causes the subscribe method to fire twice with initial value {} and the updated value, so a better practice which I found is using ReplaySubject which is pretty much similar to behavior subject but doesn't need an initial value , for simple use of replay subject, refer: https://stackoverflow.com/questions/44693438/behaviour-subject-initial-value-null – Mithil Mohan Aug 06 '18 at 09:22