36

I came across a weird issue where the Angular2's (RC1) Http service executes the http.post call twice. I've debugged my app and I know for a fact this is not a click event issue. All the calls that lead up to the core service call

public create(json: Object, params?: Object): Observable<T> {
    let body = JSON.stringify([json]);
    let headers = this.getHeaders();
    let options = new RequestOptions({ headers: headers });

    return this._http.post(this.createURL(this.getCreateURL(), [], params), body, options)
    .map(res => this.handleObjectResponse(res));
}

are run once. Then when I started tracing the issue I found out that my handler this.handleObjectResponse gets executed twice. So I delved further and reached @angular/http/src/backends/xhr_backend.ts where they do this

constructor(req: Request, browserXHR: BrowserXhr, baseResponseOptions?: ResponseOptions) {
    this.request = req;
    this.response = new Observable<Response>((responseObserver: Observer<Response>) => {
        let _xhr: XMLHttpRequest = browserXHR.build();
        _xhr.open(RequestMethod[req.method].toUpperCase(), req.url);
        // load event handler
        ...
        ..

So I put a breakpoint on this.request = req; and then another breakpoint on let _xhr: XMLHttpRequest = browserXHR.build(); and I found out I hit the first breakpoint once but then I hit the second breakpoint from the callback twice.

This has been driving me nuts so I wanted to check whether anyone familiar with the angular2 internals could shed some light whether this looks like a bug or something that I've done wrong.

In my code I've created some abstract generic service classes: GenericService and FullService which extends GenericService. Both of these are abstract and use generics and the real service classes that get injected in the different components all extend either GenericService or FullService. Do you guys think this setup could possibly be responsible for the double post executions?

All ideas are appreciated!

Thanks in advance!

P.S.

This doesn't happen with gets but it also happens with puts.

Sangwin Gawande
  • 7,658
  • 8
  • 48
  • 66
RVP
  • 2,330
  • 4
  • 23
  • 34
  • Can you share the calling code? maybe there's a multiple subscribe on the observable, in this case `return this._http.post(this.createURL(this.getCreateURL(), [], params), body, options) .map(res => this.handleObjectResponse(res)).share();` should solve it https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/share.md – teleaziz May 15 '16 at 17:47
  • 2
    Holy crap! Thank you so much - that did indeed resolve it. Now I just need to wrap my head around what I've done to cause this in the first place. Feel free to post this as an answer so I can accept it and thanks a ton again!!!! – RVP May 15 '16 at 17:52

3 Answers3

73

The http service returns a cold observable that get executed on every subscribe, you want to convert it to a hot observable that get only executed on the first subscribe and share the same value for subsequent subscribes.

To convert it all you have to do is share it:

return this._http.post(this.createURL(this.getCreateURL(), [], params), body, options)
.map(res => this.handleObjectResponse(res))
.share();
teleaziz
  • 2,220
  • 1
  • 19
  • 25
  • 1
    This comment actually helped me fix another problem in the latest RC of Angular2. Thanks man. Observables are a beautiful concept, but a little tricky to get into. – Bjorn 'Bjeaurn' S Jul 21 '16 at 06:53
  • 4
    Then they should be replaced with non-tricky code because "tricky" is going backward, making things worse, versus perfectly working – Rick O'Shea Sep 30 '16 at 21:52
  • 3
    so I suppose ALWAYS putting share() after a http call is a good practice if you return that from the method – Simon_Weaver Nov 29 '16 at 21:10
  • 3
    Just because I needed it, if you have a **catch()** clause the order should be: **.map(...).catch(...).share()**. – Sal Jul 19 '17 at 13:19
  • 2
    In Angular 7, I get `Property 'share' does not exist on type 'Observable'`. The solution is to `import { share } from 'rxjs/operators'` then `http.post().pipe(share())` – Jeremy Thille Sep 11 '19 at 08:35
  • 1
    @Jeremy Thille: Just saved my life - no wonder everybody is running away from Angular to React, in 2020. teleaziz: thanks for the answer! – Pedro Ferreira Jan 04 '20 at 21:46
2

This was happening to me because I have (key.enter)="someSubmitFunction()" on one of the input fields of a form. When I hit enter on this field the form would submit twice. Apparently, this wasn't needed. When I removed this, the form would still submit when I hit enter, but now only once.

mad_fox
  • 3,030
  • 5
  • 31
  • 43
0
its happening because HTTP OPTIONS executed first, and you have to restrict unwanted HTTP method before executing your Logic, always use isset method,see example below

 if(isset($_POST))
 {
    $name = $_POST["name"];
    $country = $_POST["country"];

    $sql = 'INSERT INTO user values("' . $name . '","' . $country . '")';

            if ( $conn->query($sql)=== TRUE) 
            {
                $outp =  "Inserted " .  $name . "  and  "  . $country;
                echo json_encode($outp);
            } else {
                echo json_encode("Error: " . $sql . "<br>" . $conn->error);
            }
        }


here it will insert row in table only when its POST METHOD.
Jayesh L
  • 137
  • 2
  • Thank you for an important clue. Better articulated here: https://stackoverflow.com/a/41671980/1779772 isset in php didn't work for since I was passing an object into php from Angular. But the idea to check to see of the information had been passed was useful. – Reid May 04 '18 at 16:37