Share data between components using a service in Angular2

mrsan22 picture mrsan22 · Mar 3, 2017 · Viewed 25.4k times · Source

I am developing an application using angular2. I have a scenario where I need to pass complex data (array of objects) from one component to another component(they are not parent-child, they are two separate components) while routing(using router.navigate()). I have googled this and most of the results describe the scenario of components that are parent-child. I have gone through the results and found these ways to pass the data.

1) Create Shared Service 2) Pass as route parameters

2nd approach works for me (even though, I don't like that when I have complex data as explained above). I am not able to share the data using shared service. So my questions is, does passing data between components using services only works when components are in parent-child relationship? Also, let me know if there are other prefered ways to pass data between one component and other?

Updated: I am adding bit of code from my scenario. Please let me know my mistake as to why passing data through shared services is not working.

I have 2 components: 1) SearchComponent 2) TransferComponent I am setting the data in SearchComponent and want to access the data in TransferComponent through utilityService.

Utility Service:

import {Injectable} from "@angular/core";

@Injectable()
export class UtilityService{
    constructor(){

    }
    public bioReagentObject = [];

    routeBioReagentObj(bioReagentObj){
        console.log("From UtilityService...", bioReagentObj);
        for (let each of bioReagentObj)
            this.bioReagentObject.push(each)
        // console.log("From UtilityService",this.bioReagentObject);
    }

    returnObject(){
        console.log("From UtilityService",this.bioReagentObject);
        return this.bioReagentObject
    }



}

searchcomponent.ts

    import {UtilityService} from "../services/utilityservice.component";
    import {Component, OnInit, OnDestroy, Input} from '@angular/core';

@Component({
    selector: 'bioshoppe-search-details',
    providers: [UtilityService],
    templateUrl: 'app/search/searchdetails.component.html',
    styleUrls: ['../../css/style.css']
})

export class SearchDetailsComponent implements OnInit, OnDestroy {
    constructor(private route: ActivatedRoute,
                private utilityService: UtilityService,
                private router: Router) {

    }
    @Input() selected: Array<String> = [{barcode:123, description:"xyz"}];

       //This method is called from .html and this.selected has the data to be     shared.
       toTransfer() {
        this.utilityService.routeBioReagentObj(this.selected);
        this.router.navigate(['/transfer']);

    }
}

TransferService.ts

import {Component, Input, OnInit, OnDestroy} from '@angular/core';
import {TransferService} from "../services/transferservice.component";
import {UserService} from "../services/userservice.component";
import {SearchService} from "../services/searchservice.component";
import {ActivatedRoute} from '@angular/router';
import {UtilityService} from "../services/utilityservice.component";


@Component({
    selector: 'bioshoppe-transfer',
    providers: [TransferService, UserService, SearchService, UtilityService],
    templateUrl: 'app/transfer/transfer.component.html',
    styleUrls: ['../../css/style.css', '../../css/transfer.component.css']
})

export class TransferComponent implements OnInit, OnDestroy{
    constructor(private transferService: TransferService,
                private userService: UserService,
                private searchService: SearchService,
                private utilityService: UtilityService,
                private route: ActivatedRoute
    ) {

    }
ngOnInit() {
// I am trying to access the data here, but it print "undefind"
console.log("From Transferpage",this.utilityService.returnObject());
}
}

I am sure something is wrong, but it's just that I am not able to figure it out.Any help is appreciated.

Answer

AngularChef picture AngularChef · Mar 3, 2017

camaron is right. Your mistake is that you declare UtilityService twice:

  1. Once in the providers of SearchComponent.
  2. Once in the providers of TransferComponent.

You should declare the service ONLY ONCE to make sure both components get the same instance. For this you can choose between either of these options:

  1. Declare the service in the providers of a @NgModule which has both SearchComponent and TransferComponent in its declarations. 9 times out of 10 this is the right solution!
  2. Declare the service in the providers of a @Component which is a parent of both SearchComponent and TransferComponent. This might not be feasible depending how your component tree looks.

Following option #1, you end up with:

@NgModule({
  imports: [CommonModule, ...],

  // Look, the components injecting the service are here:
  declarations: [SearchComponent, TransferComponent, ...],

  // Look, the service is declared here ONLY ONCE:
  providers: [UtilityService, ...]
})
export class SomeModule { }

Then inject UtilityService in your components' constructors WITHOUT REDECLARING IT in the components's providers:

@Component({
  selector: 'foo',
  template: '...',
  providers: []  // DO NOT REDECLARE the service here
})
export class SearchComponent {
  constructor(private utilityService: UtilityService) { }
}

@Component({
  selector: 'bar',
  template: '...',
  providers: []  // DO NOT REDECLARE the service here
})
export class TransferComponent {
  constructor(private utilityService: UtilityService) { }
}