I am new to Angular. I just finished developing my angular web application. When I use ng serve to serve my application during production, everything works fine. I added angular universal. Now when I run any of npm run dev:ssr or npm run build:ssr && npm run serve:ssr, my application will refuse to open, throwing NetworkError response in the console. I noticed this error occurs for the number of times http requests where sent via class 'constructors(){..}'. I have browsed through several solution but couldn't get a clue of what I'm not doing right. My backend is developed with nodejs and express. I'll appreciate any help I can get.
Here is a full example of the error response I always get in the console.
ERROR NetworkError
at XMLHttpRequest.send (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:200768:19)
at Observable._subscribe (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:19025:17)
at Observable._trySubscribe (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:186304:25)
at Observable.subscribe (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:186290:22)
at scheduleTask (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:105897:32)
at Observable._subscribe (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:105959:13)
at Observable._trySubscribe (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:186304:25)
at Observable.subscribe (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:186290:22)
at subscribeToResult (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:196385:23)
at MergeMapSubscriber._innerSub (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:191575:116)```
I was still getting this ERROR NetworkError but I found another way to make this error go away. I think this answer is relevant since I was getting the same error posted above. If this can help anyone with that same server error then that's great.
If the api request is made to the server OnInit when reloading check isPlatformBrowser first when using ng-universal example.
import { Component, OnInit, PLATFORM_ID, Inject } from '#angular/core';
import { isPlatformBrowser } from '#angular/common';
import { HttpClient, HttpHeaders } from '#angular/common/http';
export class HomeComponent implements OnInit {
public testBrowser : boolean;
public data : any;
constructor(private http: HttpClient, #Inject(PLATFORM_ID) platformId: string) {
this.testBrowser = isPlatformBrowser(platformId);
}
ngOnInit() {
if (this.testBrowser) {
//avoid server NETWORK error
this.data = this.http.get('/api');
}
}
}
I was getting this same error trying to make server calls from the client before checking isPlatformBrowser === true first OnInit and this solved my problem. Hopefully this can help this bug.
For reference this answer helped me squash this long standing bug. https://stackoverflow.com/a/46893433/4684183
I am getting the same error. Try to remove TransferHttpCacheModule from your app.module and create your own custom http transfer interceptor file.
I made a file called transfer-state.interceptor.ts and then added it to app.module providers:[] to handle this. The examples below will show how I hooked it up. I am not sure if this will definitely work for you but it did make that error go away for me.
//app.module.ts
import { BrowserModule, BrowserTransferStateModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { HttpClientModule, HTTP_INTERCEPTORS } from "#angular/common/http";
//import {TransferHttpCacheModule } from '#nguniversal/common';
import { AppRoutingModule } from './app-routing/app-routing.module';
import { AppComponent } from './app.component';
import { HomeComponent } from './modules/home/home.component';
import { SliderComponent } from './components/slider/slider.component';
import { WindowRefService } from './services/window-ref.service';
//import { TransferHttpInterceptorService } from './services/transfer-http-interceptor.service';
import { TransferStateInterceptor } from './interceptors/transfer-state.interceptor';
import { ServiceWorkerModule } from '#angular/service-worker';
import { environment } from '../environments/environment';
#NgModule({
declarations: [
AppComponent,
HomeComponent,
SliderComponent
],
imports: [
BrowserModule.withServerTransition({ appId: 'serverApp' }),
BrowserTransferStateModule,
AppRoutingModule,
HttpClientModule,
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production })
],
providers: [
WindowRefService,
{
provide: HTTP_INTERCEPTORS,
useClass: TransferStateInterceptor,
multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
This is one version of a custom transfer state file but there are a few ways to do this if this one doesn't work.
//transfer-state.interceptor.ts
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from '#angular/common/http';
import { Inject, Injectable, PLATFORM_ID } from '#angular/core';
import { Observable, of } from 'rxjs';
import { StateKey, TransferState, makeStateKey } from '#angular/platform-browser';
import { isPlatformBrowser, isPlatformServer } from '#angular/common';
import { tap } from 'rxjs/operators';
#Injectable()
export class TransferStateInterceptor implements HttpInterceptor {
constructor(
private transferState: TransferState,
#Inject(PLATFORM_ID) private platformId: any,
) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// For this demo application, we will only worry about State Transfer for get requests.
if (request.method !== 'GET') {
return next.handle(request);
}
// Use the request url as the key.
const stateKey: StateKey<string> = makeStateKey<string>(request.url);
// For any http requests made on the server, store the response in State Transfer.
if (isPlatformServer(this.platformId)) {
return next.handle(request).pipe(
tap((event: HttpResponse<any>) => {
this.transferState.set(stateKey, event.body);
})
);
}
// For any http requests made in the browser, first check State Transfer for a
// response corresponding to the request url.
if (isPlatformBrowser(this.platformId)) {
const transferStateResponse = this.transferState.get<any>(stateKey, null);
if (transferStateResponse) {
const response = new HttpResponse({ body: transferStateResponse, status: 200 });
// Remove the response from state transfer, so any future requests to
// the same url go to the network (this avoids us creating an
// implicit/unintentional caching mechanism).
this.transferState.remove(stateKey);
return of(response);
} else {
return next.handle(request);
}
}
}
}
If you want to add custom cache to this you can by installing memory-cache but I haven't tried that out yet. For more references these articles helped me out a lot and maybe they can help you too.
https://itnext.io/angular-universal-caching-transferstate-96eaaa386198
https://willtaylor.blog/angular-universal-for-angular-developers/
https://bcodes.io/blog/post/angular-universal-relative-to-absolute-http-interceptor
If you haven't you may need to add ServerTransferStateModule to your app.server.module file.
//app.server.module
import { NgModule } from '#angular/core';
import {
ServerModule,
ServerTransferStateModule
} from "#angular/platform-server";
import { AppModule } from './app.module';
import { AppComponent } from './app.component';
#NgModule({
imports: [
AppModule,
ServerModule,
ServerTransferStateModule
],
bootstrap: [AppComponent],
})
export class AppServerModule {}
good luck!
I was struggling with this error for days until I found this article About how to create a relative to absolute interceptor
here's the link
https://bcodes.io/blog/post/angular-universal-relative-to-absolute-http-interceptor
I created "universal-relative.interceptor.ts" file at my src folder
put this interceptor code in "universal-relative.interceptor.ts" file
import { HttpHandler, HttpInterceptor, HttpRequest } from '#angular/common/http';
import { Inject, Injectable, Optional } from '#angular/core';
import { REQUEST } from '#nguniversal/express-engine/tokens';
import { Request } from 'express';
// case insensitive check against config and value
const startsWithAny = (arr: string[] = []) => (value = '') => {
return arr.some(test => value.toLowerCase().startsWith(test.toLowerCase()));
};
// http, https, protocol relative
const isAbsoluteURL = startsWithAny(['http', '//']);
#Injectable()
export class UniversalRelativeInterceptor implements HttpInterceptor {
constructor(#Optional() #Inject(REQUEST) protected request: Request) {}
intercept(req: HttpRequest<any>, next: HttpHandler) {
if (this.request && !isAbsoluteURL(req.url)) {
const protocolHost = `${this.request.protocol}://${this.request.get(
'host'
)}`;
const pathSeparator = !req.url.startsWith('/') ? '/' : '';
const url = protocolHost + pathSeparator + req.url;
const serverRequest = req.clone({ url });
return next.handle(serverRequest);
} else {
return next.handle(req);
}
}
}
Go to your "app.server.module.ts" file
add your interceptor like this
import { NgModule } from '#angular/core';
import {
ServerModule,
ServerTransferStateModule,
} from "#angular/platform-server";
import { AppModule } from './app.module';
import { AppComponent } from './app.component';
import { UniversalRelativeInterceptor } from 'src/universal-relative.interceptor';
import { HTTP_INTERCEPTORS } from '#angular/common/http';
#NgModule({
imports: [AppModule, ServerModule, ServerTransferStateModule],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: UniversalRelativeInterceptor,
multi: true,
},
],
bootstrap: [AppComponent],
})
export class AppServerModule {}
And the error was GONE!
For me simply the error was that my API variable was undefined, because of the Angular SSR life-cycle. The data was only available after the browser module loaded.
I was using something like
this.isBrowser$.subscribe(isBrowser => { ... });
to set the appropriate api endpoint.
As David replied in the original issue, in my case was the resourceUrl variable that I was using, was not absolute for production environment.
environment.ts
export const environment = {
resourceUrl: 'http://localhost:8082/api/site',
siteId: '1111'
};
Like you see, for development, I was using an absolute url "http://localhost:8082/api/site" for resourceUrl environment variable. Ofcourse this was working on development mode.
environment.prod.ts
export const environment = {
resourceUrl: '/api/site',
siteId: '1111'
};
In production mode I was using a relative url (/api/site), and this was causing the issue while running "serve:ssr" which is production.
return this.http.get<ISomething>(`${environment.resourceUrl}/home/${environment.siteId}`);
So I changed environment.prod.ts to use an absolute URL. Then the issue was gone.
I am adding this reply, since maybe someone doesnt look at David comment. Thanks David.
In case someone needs, if you are using ng-universal, and because the server side rendering caused the error, then you can simply use
if (typeof window === 'object') {
// your client side httpClient code
}
I have an Angular 7 project with a PHP Laravel API backend that is returning json from get requests.
return Response::json($genres);
I'm using httpClient in a service which is being called from a component (see both below). When I console log the data it is a string, not a json object. Also I am unable to access any of the properties that it has because it is just a long string.
In most online examples people used map and then pipe but those are both deprecated now and apparently httpClient just returns JSON as default but that is not what appears to be happening in this case.
Could someone give me a solution to this? I need access to the data in the response as JSON.
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class ApiService {
constructor(private http:HttpClient) { }
getGenres(){
return this.http.get('http://www.localhost:7888/api/genres', this.genre);
}
}
import { Component, OnInit } from '#angular/core';
import { ApiService } from '../../services/api.service';
#Component({
selector: 'app-genres',
templateUrl: './genres.component.html',
styleUrls: ['./genres.component.css']
})
export class GenresComponent implements OnInit {
constructor(private apiService: ApiService) { }
ngOnInit() {
// make http request to http://www.localhost:7888/api/genres
this.apiService.getGenres().subscribe(data => {
console.log(data);
},
error => {
console.log(error);
});
}
}
Trying to make a simple POST to my server, with some data and have it return some JSON data. (Currently, it runs through the call but I see nothing on my network tab that it actually tried to make it?)
My Service (api.service.ts)
import { Injectable } from '#angular/core';
import { Headers, Http, Response} from '#angular/http';
import {Observable} from 'rxjs/Rx';
// Import RxJs required methods
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
#Injectable()
export class ApiService {
private headers = new Headers({'Content-Type': 'application/json'});
private myUrl = 'https://www.mywebsite.com'; // URL to web api
private payLoad = {"thingy1":"Value1", "thingy2":"value2"};
constructor(private http:Http) { }
getData () {
this.http.post(this.myUrl, JSON.stringify(this.payLoad), this.headers)
.map((res:Response) => res.json())
.catch((error:any) => Observable.throw(error.json().error || 'Server error'));
}
}
Then I just have the getData method ran in a header component just to fire it.
(header.component.ts)
import { Component, OnInit } from '#angular/core';
//Service
import { ApiService } from '../services/api.service';
#Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss']
})
export class HeaderComponent implements OnInit {
constructor(private service:ApiService) {
this.service.getData(); //Fire getData from api.service
}
ngOnInit() { }
}
Observable are lazy, even if you initialize them, they will not be executed. At least one subscriber has to explicitly subscribe to that observable to listen to that stream. In short: They will only be executed when you subscribe to it.
this.service.getData().subscribe(
(data) => console.log(data)
);
Make sure you are subscribing your observable data. The RxJS observer has 2 parts,
next() - it push data which is done by angular itself.
subscribe - you need in your component to get data from observable.
Try something below
this.service.getData().subscribe(
(data) => {
//process your data here
}
);
Screenshot of error:
Code where error exists:
import {Component, OnInit} from 'angular2/core';
import {Router} from 'angular2/router';
import {Hero} from './hero';
import {HeroService} from './hero.service';
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
import {HeroesComponent} from './heroes.component';
import {HeroDetailComponent} from './hero-detail.component';
import {DashboardComponent} from './dashboard.component';
import {SpreadSheetComponent} from './spreadsheeteditall.component';
import {SwitchUsersComponent} from './SwitchUsers.component';
import {BiddingPageComponent} from './BiddingPage.component';
import { Injectable } from 'angular2/core';
import { Jsonp, URLSearchParams } from 'angular2/http';
#Component({
selector: 'SearchAndDisplayComponent',
templateUrl: 'app/SearchDisplay.component.html',
styleUrls: ['app/SearchDisplay.component.css'],
providers: [HeroService],
directives: [ROUTER_DIRECTIVES]
})
#Injectable()
export class SearchAndDisplayComponent{
constructor(private jsonp: JSON) {}
search (term: string) {
// let ebayURL = 'http://en.wikipedia.org/w/api.php';
var params = new URLSearchParams();
params.set('search', term); // the user's search value
params.set('action', 'opensearch');
params.set('format', 'json');
params.set('callback', 'JSONP_CALLBACK');
// TODO: Add error handling
return this.jsonp
.get({ search: params })
.map(request => <string[]> request.json()[1]);
}
}
Context of the problem:
I am trying to create a search bar for a website that is basically a clone of ebay.
Here is a question I posted earlier with links to the whole project (plunker/full project zipped)
Search bar that hides results that aren't typed into it
HTML code of how I'm trying to display it by the click of a button next to the search bar:
<button (click)="search(term)">Search</button>
You need to inject the Jsonp class instead of the JSON one in the constructor. The Jsonp object will allow you to execute JSONP requests.
import {Jsonp} from 'angular2/http';
(...)
export class SearchAndDisplayComponent{
constructor(private jsonp: Jsonp) {} // <-----
(...)
}
JSON is javascript namespace object. You have imported Jsonp from the module angular2/http, so in constructor of yout service change JSON to Jsonp. And don't forget to add Jsonp provider to some component above at component hierarchy.
I am using Angular 2 for my web application. Now I am trying to populate a checkbox list from backend service call. This is what I am trying.
main.ts
import {bootstrap} from 'angular2/platform/browser';
import {ROUTER_PROVIDERS} from 'angular2/router';
import {HTTP_PROVIDERS} from 'angular2/http';
import 'rxjs/add/operator/map';
import {DataService} from './service'
import {AppComponent} from './app.component';
bootstrap(AppComponent, [ROUTER_PROVIDERS,HTTP_PROVIDERS,DataService]);
service.ts
import {Http, Response} from 'angular2/http'
import {Injectable} from 'angular2/core'
#Injectable()
export class DataService {
http: Http;
constructor(http: Http) {
this.http = http;
}
getCheckboxList() {
return this.http.get('http://localhost:8080/test/getList').map((res: Response) => res.json());
}
}
checkbox.ts
import {Component} from 'angular2/core';
import {DataService} from '../service';
#Component({
templateUrl: 'views/checkboxlist.html'
})
export class CheckboxComponent {
message = "hello";
constructor(dataService: DataService) {
dataService.getCheckboxList().subscribe(function(res) {
console.log(res.result);
this.list = res.result;
console.log(this.list);
})
}
}
checkboxlist.html
<div>
<label *ngFor="#item of list">
<input type="checkbox">{{item.value}}
</label>
</div>
Backend service is successful and returns a response and line console.log(this.list); prints an object array (HTTP response). Although, it doesn't display the checkbox list and there is not any error on the console log.
Does anyone have any idea what's wrong with my code?
Thank You
You should use an arrow function in your component to be able to use the lexical this. In this case, the this keyword will correspond to the instance of the component. In your case, you use a "normal" function and the this keyword used in it doesn't correspond to the component instance...
dataService.getCheckboxList().subscribe((res) => { // <--------
console.log(res.result);
this.list = res.result;
console.log(this.list);
})
See this plunkr: https://plnkr.co/edit/r1kQXlBVYvuO5fvQJgcb?p=preview.
See this link for more details:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
I would expect an error message in the browser console
Try the safe-navigation ?.
<input type="checkbox">{{item?.value}}