I am trying to create a generic DataService
with hateoas implementation.
There's a REST api /root
which provides all the hateoas link.
For example,
{
_links : {
login : {
href : '/login',
method : 'POST'
},
orders : {
href : '/orders',
method : 'GET'
},
orderById : {
href : '/order/{id}',
method : 'GET'
}
.......
}
}
On application load, The DataService
should make a call to /root
api and store the response in an instance variable, say rootLinks
. It should be available for the entire session.
Then DataService
should provide a followLinkByName
method which get's the href
from available rootLinks
and triggers a new http request.
const rootUrl: string = '/root';
const baseUrl: string = 'http://localhost:8080';
@Injectable()
export class DataService {
private observable$: Observable<any>;
private rootLinks: any;
constructor(private http: Http) {
this.ngOnInit();
}
ngOnInit() {
this.getRootLinks().subscribe();
}
private getRootLinks() {
if (this.rootLinks) {
return Observable.of(this.rootLinks);
} else if (this.observable$) {
return this.observable$;
} else {
this.observable$ = this.http.get(rootUrl).map(this.extractRootLinkData);
return this.observable$;
}
}
private extractRootLinkData(response: Response) {
this.observable$ = null; // LINE 1
let data = response.json();
this.rootLinks = data._links;
}
private extractData(response: Response) {
let body = response.json();
return body;
}
followLinkByName(linkName: String): Observable<any> {
let link;
if (this.observable$) { // LINE 2
return this.observable$.map((res) => {
link = res._links[linkName];
// make a http request and return the response
});
} else {
link = this.rootLinks[options.linkName];
options.link = link;
// make a http request and return the response
}
}
}
I have added this DataService
in core module's
providers array,
and core module
is imported to the app module
.
Now there's a LoginComponent
from pages
module which uses this DataService
to login. Though in line 1, the observable$
is assigned to null, it is available at line 2 when a call is made from LoginComponent
.
Snapshots,
1. on application load it invoke /root
api and once the data is available, assigns the observable to null.
Since the this.http.get(rootUrl)
call is asynchronous are you sure you're not losing this
context when you're using .map(this.extractRootLinkData)
?
I think when the extractRootLinkData()
method is called as a callback to map()
the this
context is equal to window
. So you're executing statement this.observable$ = null
on window
which doesn't exist anyway.
You can use an anonymous function instead:
this.observable$ = this.http.get(rootUrl).map(response => this.extractRootLinkData(response));
... or bind the this
context:
this.observable$ = this.http.get(rootUrl).map(this.extractRootLinkData.bind(this));
Also see: How to access the correct `this` context inside a callback?