0

I have a express server and Angular 4 application that cohabitate in one project. It is based upon this starter: https://github.com/vladotesanovic/angular2-express-starter

The issue I am having is that when Angular does a post or get, it fires twice.
enter image description here

You can imagine how this is an issue when doing a post!

This is my component:

import { Component, OnInit } from '@angular/core';
import { Settings } from '../shared/settings/settings';
import { SettingsService } from '../shared/settings/settings.service';
import { Observable } from 'rxjs/Observable';

@Component({
    templateUrl: './welcome.component.html',
    styleUrls: ['./welcome.component.scss']
})
export class WelcomeComponent implements OnInit {
    settings:  Observable<Settings[]>;
    error: any;

    constructor(
        private settingsService: SettingsService) {
    }

    ngOnInit() {
        this.getSettings();
    }

    getSettings() {
        this.settings = this.settingsService.getSettings();
      }
}

The service is:

import { Injectable } from '@angular/core';
import { Headers, Http, Response } from '@angular/http';
import 'rxjs/add/operator/catch';
import { Settings } from './settings';
import {Observable} from 'rxjs/Rx';
import { ErrorComponent } from '../error/error.component';
import { MdDialog, MD_DIALOG_DATA } from '@angular/material';

@Injectable()
export class SettingsService {
  constructor(private http: Http,  private dialog: MdDialog) { }

extractData(res: Response) {
    return res.json();
}

getSettings(): Observable<Settings[]> {
    return this.http.get('/api/getSettings').map(this.extractData).catch(error => this.handleError(error));
}


private handleError(error: any): Promise<any> {
    const dialog = this.dialog.open(ErrorComponent, {
        data: {
            error: error
        }
    });

    return Promise.reject(error.message || error);
}
}

The app.module is:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpModule } from '@angular/http';
import { MaterialModule } from '@angular/material';
import 'hammerjs';
import { AppRoutingModule } from './app-routing.module';
import { FlexLayoutModule } from '@angular/flex-layout';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';

import { SettingsService } from './shared/settings/settings.service';

import { AppComponent } from './app.component';
import { WelcomeComponent } from './welcome/welcome.component';
import { OrderComponent } from './order/order.component';
import { ErrorComponent } from './shared/error/error.component';
import { SuccessComponent } from './shared/success/success.component';

import { TextMaskModule } from 'angular2-text-mask';
import { LoadingBarHttpModule } from '@ngx-loading-bar/http'; //https://github.com/aitboudad/ngx-loading-bar
import {enableProdMode} from '@angular/core';



@NgModule({
    declarations: [
        AppComponent,
        WelcomeComponent,
        OrderComponent,
        ErrorComponent,
        SuccessComponent
    ],
    imports: [
        BrowserModule,
        HttpModule,
        MaterialModule,
        AppRoutingModule,
        FlexLayoutModule,
        BrowserAnimationsModule,
        FormsModule,
        ReactiveFormsModule,
        TextMaskModule,
        LoadingBarHttpModule
    ],
    providers: [
        SettingsService
    ],
    bootstrap: [
        AppComponent
    ],
    entryComponents: [
        ErrorComponent,
        SuccessComponent
    ]

})
export class AppModule { }

For the server side, I can add more code or post the entirety to GitHub, but the proxy.conf.json and package.json are essentially the same as his starter. My guess is that a module is being double loaded or perhaps I am using Observables wrong?

package.json:

"scripts": {
    "start": "concurrently --kill-others \"npm run _server:run\" \"ng serve --aot=false --progress=false --proxy-config proxy.conf.json\"",
    "lint:client": "ng lint",
    "lint:server": "tslint './server/**/*.ts' -c ./server/tslint.json --fix",
    "test:client": "ng test",
    "e2e:client": "ng e2e",
    "build": "ng build --prod --sm=false --aot --output-path=dist/client && npm run _server:build",
    "_server:run": "tsc -p ./server && concurrently \"tsc -w -p ./server\" \"nodemon --delay 5 dist/server/bin/www.js\" ",
    "_server:build": "tsc -p ./server",
    "postinstall": "npm run build"
  },

proxy.json.conf:

{
  "/api": {
    "target": "http://localhost:4300",
    "secure": false
  }
}

Edit to show a different approach.

Even if I do this with Promises, I still have the same issue, which makes me think it is something in the router or app.module still.

export class WelcomeComponent implements OnInit {
    settings: Settings[];
    error: any;

    constructor(
        public dialog: MdDialog,
        private router: Router,
        private settingsService: SettingsService) {
        console.log('calling constructor');
    }


    async ngOnInit(): Promise<void> {
        console.log('calling onInit');
        this.getSettings();
    }


    async getSettings(): Promise<void> {
        this.settingsService.getSettings()
            .then(result => this.settings = result)
            .catch(error => this.handleError(error));
    }

}

Here is the service

async getSettings(): Promise<Settings[]> {
      console.log('calling SettingsService');
      try {
          const response = await this.http
              .get('/api/getSettings')
              .toPromise();
          return response.json() as Settings[];
      } catch (error) {
          await this.handleError(error);
      }
  }

enter image description here

Matt Knight
  • 499
  • 2
  • 7
  • 20
  • Lots of possibilities. I'd focus on tracing the stack. Is the service `getSettings` called twice, and by what? Likewise is the component `getSettings` or `ngOnInit` called twice, and by what? etc. – Will Sep 06 '17 at 20:51
  • @Will Agreed, it is a hard one to track down. Even if I move the call to getSettings() out of ngOnInit() and call it manually (such as with a button), it still happens. That makes me think it is something in the app.module or in the relationship between Express and Angular. – Matt Knight Sep 06 '17 at 21:48
  • Have you checked that the first one is not just an [OPTIONS request](https://stackoverflow.com/questions/1256593/why-am-i-getting-an-options-request-instead-of-a-get-request)? Those are normal. – noppa Sep 06 '17 at 23:40
  • How many times are you binding to settings in the Welcome component with async? Unless you share the Observable the http request will be made for each binding (see [pitfall 2](http://blog.angular-university.io/angular-2-rxjs-common-pitfalls/) for a fuller explanation) – JayChase Sep 07 '17 at 00:17
  • @noppa It isn't the options request. – Matt Knight Sep 07 '17 at 12:54
  • @JayChase - I am binding the settings only once. Also, I should add that even if I move away from Observables and use straight up promises, it has the same behavior. – Matt Knight Sep 07 '17 at 13:16

2 Answers2

0

The fact you are seeing the call firing twice in the chrome dev tools means that the issue is on the client side.

My suggestion is to subscribe to the call instead of assigning the returned observable to a local property. Then you can make a break point at entry point of you method that calls the service. Once hit, have a look at the call stack to see who is firing the call. I would guess that you will see the OnInit method calling getSettings() twice. To which case I would assume that your component is being created twice for some reason in your application.

Mehdi
  • 2,263
  • 1
  • 18
  • 26
  • Even if I change to promises, the issue still occurs. The constructor, onInit, and SettingsService are all being called only once. – Matt Knight Sep 07 '17 at 13:15
0

The culprit turned out to be a 3rd party: https://github.com/aitboudad/ngx-loading-bar

If you are using ngx-loading-bar, LoadingBarHttpModule will cause this behavior. I haven't managed to get LoadingBarHttpClientModule to work yet.

Matt Knight
  • 499
  • 2
  • 7
  • 20