Prevent duplicate HTTP requests in Angular 2 API-driven tree-view
<p>I have an Angular 2 app which calls a JSON API to load data into a nested HTML list (<code>&lt;ol&gt;</code>).  Essentially a dynamically generated tree-view.  Since the data-set from the API will eventually become very large the tree is populated progressively when the user opens a branch (user opens a branch, the API is called with the id of the node which has been opened, the API returns a JSON feed of the direct children of than node, Angular binds the returned JSON to some new HTML <code>&lt;li&gt;</code> elements and the tree expands to show the new branch).</p>

<p>You can see it in action in this <a href="http://plnkr.co/edit/100nwxwXuCONelmHUTF4?p=preview" rel="noreferrer">Plunkr</a>.  It uses a recursive directive and works nicely.  <em>Currently, because I can't open the actual API up to public requests, it's just calling a static JSON feed, hence the returned data for each of the nodes is just repeated, but hopefully you get the idea.</em></p>

<p>The issue I'm now seeking to overcome is to prevent extraneous HTTP calls when a branch is closed and then re-opened.  Having read the <a href="https://angular.io/docs/ts/latest/guide/server-communication.html#!#rxjs" rel="noreferrer">HTTP client docs</a> I was hoping that this would be as simple as modifying the method which subscribes to the data service to chain the <code>.distinctUntilChanged()</code> method to the <code>app/content-list.component.ts</code> file, as follows:</p>

<pre><code>getContentNodes() {
    this._contentService.getContentNodes(this._startNodeId)
        .distinctUntilChanged()
        .subscribe(
            contentNodes =&gt; this.contentNodes = contentNodes,
            error =&gt;  this.errorMessage = &lt;any&gt;error
        );
}
</code></pre>

<p>However, when I open up the browser network inspector, it still makes a call to the API each time the same tree branch is re-opened.</p>

<p>Could anyone advise how to resolve this?</p>

<p>Many thanks.</p>

<p><strong>EDIT:</strong></p>

<p>I'm trying to implement @Thierry Templier's answer below; caching the data returned by the API. So the content service is now:</p>

<pre><code>import {Injectable}                 from 'angular2/core';
import {Http, Response}             from 'angular2/http';
import {Headers, RequestOptions}    from 'angular2/http';
import {ContentNode}                from './content-node';
import {Observable}                 from 'rxjs/Observable';

@Injectable()
export class ContentService {
    constructor (private http: Http) {}

    private _contentNodesUrl = 'app/content.json';

    _cachedData: ContentNode[];

    getContentNodes (parentId:number) {
        if (this._cachedData) {
            return Observable.of(this._cachedData);
        } else {
            return this.http.get(this._contentNodesUrl + parentId)
                .map(res =&gt; &lt;ContentNode[]&gt; res.json())
                .do(
                    (data) =&gt; {
                    this._cachedData = data;
                })
                .catch(this.handleError);
        }
    }

    private handleError (error: Response) {
        console.error(error);
        return Observable.throw(error.json().error || 'Server error');
    }
}
</code></pre>

<p>However, what's happening here is that when the page loads, <code>this._cachedData</code> is returning false and the API call is firing and populating <code>this._cachedData</code> with the returned value (<code>data</code>), which is correct. However, when the tree node is opened in the web page UI, the following line is run repeatedly thousands of times until eventually the browser crashes:</p>

<pre><code>return Observable.of(this._cachedData);
</code></pre>

<p>I'm still very much at the beginning when it comes to Angular 2, so would really appreciate any pointers as to why this isn't working to help my learning.</p>