Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 2 singleton service not acting as singleton

I've got a service called TargetService that I'm injecting into various other components. This TargetService has a property called Targets which is a collection of Target objects.

My problem is that I want this collection to persist after routing to another view. My routes are working fine, but as soon as the route changes, the Service loses the content of any variables, essentially, it's re-initializing the Service. My understanding was that these injected services are to be singletons that can be passed around?

In the following example, on the TargetIndex, I click a button that populates the Targets[] object on the service (this.targetService.targets = ts;). This is working fine, then I route to the TargetShow page, and then back to this index and now this Targets[] property is empty when I want it to contain what I've already populated.

What am I missing here?

App.Module

const routes: Routes = [
  { path: '', redirectTo: 'targets', pathMatch: 'full'},
  { path: 'targets', component: TargetIndexComponent },
  { path: 'targets/:id', component: TargetShowComponent }
]
    
@NgModule({
  declarations: [
    AppComponent,
    TargetComponent,
    TargetIndexComponent,
    TargetShowComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    ReactiveFormsModule,
    HttpModule,
    RouterModule.forRoot(routes)
  ],
  providers: [TargetService],
  bootstrap: [AppComponent]
})
export class AppModule { }

TargetService

@Injectable()
export class TargetService {
  public targets: Target[];

  constructor(private http: Http) {}

  getTargets(hostname: String): Observable<Target[]> {
    return this.http.request(`url`).map(this.extractData);
  }

  private extractData(res: Response) {
    let body = res.json();
    return body || [];
  }
}

TargetIndex

@Component({
  selector: 'app-targets',
  templateUrl: './target-index.component.html',
  providers: [TargetService]
})
export class TargetIndexComponent {
  loading = false;

  constructor(private http: Http, private targetService: TargetService) {}

  loadTargets(hostname: HTMLInputElement) {
    this.loading = true;
    this.targetService.getTargets(hostname.value)
    .subscribe((ts: Target[]) => {
      this.targetService.targets = ts;
      this.loading = false;
    })
  } 
}

TargetShow

@Component({
  selector: 'app-target-show',
  templateUrl: './target-show.component.html'
  providers: [TargetService]
})
export class TargetShowComponent {
  id: string
    
  constructor(private route: ActivatedRoute, private targetService: TargetService) {
    route.params.subscribe(params => { this.id = params['id']; })
  }
}
like image 758
Cheyne Avatar asked Nov 17 '16 18:11

Cheyne


1 Answers

Try to remove TargetService from components providers, cause you already added it in module providers. When you add this service to components providers, DI creates new instances of it.

Here is quote from https://angular.io/docs/ts/latest/guide/dependency-injection.html :

When to use the NgModule and when an application component? On the one hand, a provider in an NgModule is registered in the root injector. That means that every provider registered within an NgModule will be accessible in the entire application.

On the other hand, a provider registered in an application component is available only on that component and all its children.

like image 87
Nikolai Avatar answered Nov 15 '22 22:11

Nikolai