Reputation: 2242
So right now I have an application that is set up like this:
AppModule
SubModule
Multiple Components
SecondSubModule
Multiple Components
I need to be able to make a data call to a REST API, that gets fed into both SubModule
and SecondSubModule
. I was thinking about creating a module to wrap them both and making the data call there, but I can't figure out how to make a data call in a module, or if I even should.
And if I make a component, it requires that it be added to the markup as an empty tag somewhere, which is hacky at best.
Basically I would like these "packaged" under another module that makes the necessary data call and feeds it into the other two modules, like this:
AppModule
ParentModule
SubModule
Multiple Components
SecondSubModule
Multiple Components
The goal here is to make this entire package transportable and completely detached from the AppModule
, so that it can be added to any other app by simply adding the associated files and adding it to AppModule
.
What is the correct way of doing this in Angular 2?
Upvotes: 0
Views: 896
Reputation: 2242
The answer here for anyone else coming across this is this.
This way of thinking is very Angular 1, where in you would supply data to a controller, and through inheritance pass it down.
While there is inheritance through the use of @Input
decorators, the real solution here is to create a data service that does the work for you.
The tools you will need are:
In your data service, you will import the dependencies:
// App Imports
import { Injectable } from '@angular/core';
import { Headers, Http } from "@angular/http";
import {Observable} from 'rxjs/Observable';
// RXJS Imports
import 'rxjs/observable/of';
import 'rxjs/add/operator/share';
import 'rxjs/add/operator/map';
Then you need an injectable decorator
@Injectable
Then you need your class
export class DataService {
Next comes the constructor
// Constructor
constructor(private http: Http) { }
Now we need some variables
// Variables
private data: any = null;
private observable: Observable<any>;
private headers = new Headers({"Content-Type": "application/json"});
data
will hold our data once it has been retrieved from the REST/JSON API
observable
will hold our observable, the object to which external calls will subscribe (this is the magic part)
headers
simply holds the headers we will use to send the http request.
Now we will need to define the function to get the data
// Returns data
getData() {
Now is going to come an algorithm to return the correct object depending on the state of the service.
// If we already have data
if (this.data){
// Return the data
return Observable.of(this.data);
} else if (this.observable){
// Otherwise if we have an observable, return it
return this.observable;
} else {
// Otherwise get the data
this.observable = this.http.get("/api/myApiClass", {headers: this.headers})
.map( response => {
// Clear the observable
this.observable = null;
// If the call failed
if (response.status == 400) {
// Return false
return false
} else if (response.status == 200){
// Otherwise set the data
this.data = response.json().data;
// And return the response
return this.data
}
})
.share();
// Return the observable to be subscribed to
return this.observable;
}
Notice the .share()
right near the end there. That is the last piece of the magic that ensure that we only make one HTTP call.
Now we need to provide this service in the module that contains the components that will be using this service.
First, import the data service into your module
import { DataService } from "../../services/data.service";
now declare it in your providers
providers: [ DataService ]
Now all sub-components will have access to the service as a singleton.
Now in the components that will call this data service, you will import the service making sure to include OnInit:
import {Component,OnInit} from '@angular/core';
import { DataService } from "../services/data.service";
And define the data service in your constructor
constructor(private dataService: DataService){};
Now finally, subscribe to the observable contained in the data service:
ngOnInit(){
this.dataService.getData().subscribe(data => {
this.data = data;
});
}
In the end, your files should look like this:
// App Imports
import { Injectable } from '@angular/core';
import { Headers, Http } from "@angular/http";
import {Observable} from 'rxjs/Observable';
// RXJS Imports
import 'rxjs/observable/of';
import 'rxjs/add/operator/share';
import 'rxjs/add/operator/map';
@Injectable()
export class DataService{
// Constructor
constructor(private http: Http) { }
// Variables
private data: any = null;
private observable: Observable<any>;
private headers = new Headers({"Content-Type": "application/json"});
// Returns data
getData() {
// If we already have data
if (this.data){
// Return the data
return Observable.of(this.data);
} else if (this.observable){
// Otherwise if we have an observable, return it
return this.observable;
} else {
// Otherwise get the data
this.observable = this.http.get("/api/myApiClass", {headers: this.headers})
.map( response => {
// Clear the observable
this.observable = null;
// If the call failed
if (response.status == 400) {
// Return false
return false
} else if (response.status == 200){
// Otherwise set the data
this.data = response.json().data;
// And return the response
return this.data
}
})
.share();
// Return the observable to be subscribed to
return this.observable;
}
}
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { DataService } from "../../services/data.service";
@NgModule({
imports: [
CommonModule
],
declarations: [],
exports: [],
providers: [ DataService ]
})
export class MyModule {
}
import {Component,OnInit} from '@angular/core';
import { DataService } from "../services/data.service";
@Component({
selector: 'my-selector',
template: require('./my.component.html')
})
export class MyComponent{
constructor(private dataService: DataService){};
data: any;
ngOnInit(){
this.dataService.getData().subscribe(data => {
this.data= data;
});
}
}
I know this is a little long winded, but it should be helpful to anyone out there looking to do the same thing.
Upvotes: 1