0

I am trying to share data between components using a common shared service. This is the service.

@Injectable()
export class JobService {
    public jobType=null;
    public examples=[];
}

This is my first component. The full code within the component is much too long so I've just added a ... to represent the rest of the code but inside this component the jobType and examples variables of the service are set.

@Component({
            selector: 'app-job',
            templateUrl: './job.component.html'
})
export class JobComponent implements OnInit {

          constructor(private jobService: JobService, private authService: AuthService, private router: Router) {
            }
    ...

}

and the second component is

@Component({
    selector: 'app-preview',
    template:'./preview.component.html'
})
export class PreviewComponent implements OnInit {

    jobType;
    examples;

    constructor(private jobService: JobService) {
    }  
    ngOnInit() {

        this.jobType=this.jobService.jobType;
        this.examples=this.jobService.examples;
    }

}

So the idea is that it should be able to get the jobType and examples variables that were set within the service inside JobComponent.

The service itself is provided in the module file

@NgModule({
    declarations: [
        JobComponent,
        JobListComponent,
        JobDetailsComponent,
        PreviewComponent
    ],
    imports: [
        CommonModule,
        FormsModule,
        RouterModule,
        TabsModule
    ],
    providers: [JobService]
})
export class JobModule {

}

It is my understanding that this means that JobService is instantiated only once and is shared between the components.

The problem arises within the html template of JobComponent. It contains a router link to open the PreviewComponent in a new tab i.e.

<a target="_blank" routerLink="/preview">Preview</a>

When this link is opened, the variables within JobService have already been set in JobComponent (I checked that this is true). The /preview route is associated with PreviewComponent. When the new window opens and PreviewComponents reads the JobService variables, they have not been set which leads me to believe that PreviewComponent has created an entirely new instance of JobService. However, according to Angular2 - Share data between components using services This should not happen if JobService is only provided once in the modules file. Can anyone tell me why the JobService does not seem to be shared across these two components?

user1893354
  • 5,778
  • 12
  • 46
  • 83
  • services instantiate once per window. they aren't shared between windows. You need to use local storage or session storage to transfer data, or make the new tab an actual child of the original so they can commnunicate and you can call functions on the child. – bryan60 Jan 27 '18 at 20:29

2 Answers2

2

It's because you're opening the page in a new window. The state of JobService is not being preserved. A possible solution could be passing the JobService's state to the preview component using url query parameters and rebuilding the service in the new page for example, navigating to /preview?jobType=something&examples=are,comma,separated,list or saving the state in the browser (local storage, cookies, whatever) and reading it on page initialization

Gab
  • 1,007
  • 9
  • 22
  • 1
    Thanks. The number of examples could be in the hundreds so I think that's probably too large to put in routing params but the local storage idea might work - it might even be simpler that way, thanks again! – user1893354 Jan 27 '18 at 20:21
1

To save the state of a shared resource you should use BehaviorSubjects.

@Injectable()
export class JobService {
    public jobType$: BehaviorSubject<any> = new BehaviorSubject('');
    public examples$: Behavior Subject<any[]> = new BehaviorSubject([]);
    public jobTypes = null;
    public examples = [];

    setJobType(jobType) {
        this.jobTypes = jobType;
        this.jobType$.next(this.jobTypes);
    }
    //set the same way for examples
}

Then in each of your components.

@Component({
            selector: 'app-job',
            templateUrl: './job.component.html'
})
export class JobComponent implements OnInit {

  constructor(private jobService: JobService, private authService: AuthService, private router: Router) {}

  //somewhere like ngOnInit subscribe to jobType$ and or examples$

  //on some event or trigger of the ui call the setJobType method with changes.
}


@Component({
    selector: 'app-preview',
    template:'./preview.component.html'
})
export class PreviewComponent implements OnInit {

    jobType;
    examples;

    constructor(private jobService: JobService) {
    }  
    ngOnInit() {
        this.jobService.jobType$.subscribe(result => this.jobType = result);
        this.jobService.examples$.subscribe(result => this.examples = result);
    }
}
Mike Tung
  • 4,735
  • 1
  • 17
  • 24