My question sounds similar to Cannot find the '#angular/common/http' module and Error loading #angular/common/http - angular 2 but the problem is a bit different:
I am using Angular 4.3.5 and I am trying to read data from a Web API. (This API puts out JSON data and is using SignalR and .net Core).
I have followed several tutorials and came up with this code for the class that will actually contact the service:
import 'rxjs/add/operator/map';
import { HttpClient, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import { Configuration } from './Configuration.js';
#Injectable()
export class DataService {
private actionUrl: string;
constructor(private http: HttpClient, private configuration: Configuration) {
this.actionUrl = configuration.serviceUrl;
}
//public getAll<T>(): Observable<T> {
// return this.http.get<T>(this.actionUrl);
//}
public getSingle<T>(id: number): Observable<T> {
return this.http.get<T>(this.actionUrl + id);
}
}
#Injectable()
export class CustomInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (!req.headers.has('Content-Type')) {
req = req.clone({ headers: req.headers.set('Content-Type', 'application/json') });
}
req = req.clone({ headers: req.headers.set('Accept', 'application/json') });
console.log(JSON.stringify(req.headers));
return next.handle(req);
}
}
Now, building this project (I am using Visual Studio 2017 Enterprise) and running a gulp task to transpile the .ts to .js works just fine, so do all the intellisense-tooltips - the IDE does recognize the existance of those things.
But if I open it up in a browser (doesnt matter if firefox, edge or chrome) I get the following error:
zone.js:958 GET http://localhost:3966/libs/#angular/common/bundles/common.umd.js/http 404 (Not Found)
If I edit the transpiled javascript file by hand and write common-http.umd.js there, the file is found. (This is the reason why at the top I import Configuration.js instead of Configuration - it doesnt seem to want to automatically resolve the suffix, like in some tutorials).
I hope I am not too vague, since this is my first Time asking something publically. Also I was not able to find an answer in the given questions.
Well, I found a solution, for anybody who is curious why this and similar problems exist:
I had to edit my systemjs file and add this line:
'#angular/common/http': 'npm:#angular/common/bundles/common-http.umd.js',
and it works!
Related
In my angular application I have interceptor class like that:
import { Injectable, Inject, Optional, PLATFORM_ID } from '#angular/core';
import {
HttpInterceptor,
HttpHandler,
HttpRequest,
} from '#angular/common/http';
import { REQUEST } from '#nguniversal/express-engine/tokens';
import { isPlatformServer } from '#angular/common';
#Injectable()
export class UniversalInterceptor implements HttpInterceptor {
constructor(
#Inject(PLATFORM_ID) private platformId,
#Optional() #Inject(REQUEST) private request
) {}
intercept(req: HttpRequest<any>, next: HttpHandler) {
if (isPlatformServer(this.platformId)) {
req = req.clone({ headers: req.headers.set('Cookie', this.request.headers.cookie) });
}
return next.handle(req);
}
}
I am using Angular Universal server side rendering, so I take token from server and manually set in every API call which will be called by server. Everything works fine, but is it safe? I set token cookie manually inside every API request's header, maybe that's somehow risky?
I mayb be wrong, but I think it only matters if you send cookies to an API that you don't own, in which case you might be sending sensitive information to a 3rd party.
If you don't own the API, you could try parsing the cookies string (this.request.headers.cookie) and only pass the ones that the API need
I have found this issue many times in different Angular versions but multiple sources say it is already fixed, for example, another similar question on Stackoverflow is having this answer which says that it was solved in Angular 5.2 , some other issues on Github are saying it solved in 6.0.2 , I am using Angular 6.0.3 but still I get that issue, whenever I try injecting a service that uses HttpClient inside my HttpInterceptor class (that should add a jwt token to the requests if the jwt was received in that service)
I don't get a warning or error that I have cyclic dependency, put I can see in the browser console thousands of requests that the Interceptor is calling the service & vice-versa.
What is the proper way to handle this issue? (preferably without storing jwt in localstorage)
(i.e using the interceptor & getting the jwt from the service)
My service:
....
// only showing parts that matter, HttpClient is injected here
// to be used in calling the API services
constructor(public http: HttpClient, private route: ActivatedRoute,
public translate: TranslateService, private router: Router,
public snackBar: MatSnackBar, private notification: NotificationService) {
//
My HttpInterceptor:
import { Observable } from 'rxjs';
import { Location } from '#angular/common';
import { Injectable, Injector } from '#angular/core';
import { HttpInterceptor } from '#angular/common/http';
import { HttpRequest } from '#angular/common/http';
import { HttpHandler } from '#angular/common/http';
import { HttpEvent } from '#angular/common/http';
import { HttpHeaders } from '#angular/common/http';
import { DataService } from './data/data.service';
#Injectable()
export class CustomHttpInterceptor implements HttpInterceptor {
constructor(private injector: Injector) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const dataService = this.injector.get(DataService);
console.log('hey there, we are here INTERCEPTOR');
if (dataService.jwt) {
request = request.clone({
setHeaders: {
Authorization: `Bearer ${dataService.jwt}`
}
});
}
return next.handle(request);
}
}
ng version
Angular CLI: 6.0.8
Node: 10.0.0
OS: win32 x64
Angular: 6.0.7
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, router
Package Version
-----------------------------------------------------------
#angular-devkit/architect 0.6.3
#angular-devkit/build-angular 0.6.3
#angular-devkit/build-optimizer 0.6.3
#angular-devkit/core 0.6.3
#angular-devkit/schematics 0.6.8
#angular/cdk 6.1.0
#angular/cli 6.0.8
#angular/flex-layout 6.0.0-beta.15
#angular/material 6.3.1
#ngtools/webpack 6.0.3
#schematics/angular 0.6.8
#schematics/update 0.6.8
rxjs 6.2.1
typescript 2.7.2
webpack 4.8.3
I was able to work around it by instantiating HttpClient myself inside the service:
#Injectable()
export class DataService {
private http: HttpClient;
constructor(httpBackend: HttpBackend) {
this.http = new HttpClient(httpBackend);
}
}
This broke the infinite loop issue in my case (using Angular#6.1.10).
Is there a way to handle when an Angular 2+ app is updated?
Note: NOT WHEN ANGULAR IS UPDATED for example from 4.1.0 to 4.1.2 (this not)
when i say "updated" i mean to:
When code has changed and is published to production.
when i publish an update to the system built in Angular 4, the view of clients just start to have errors because javascript of NG has changed, even have other javascript generated names.
what's the right way to handle this?
Angular has "something" official for say to the Browser when to update code/resources?
or something like that.
thanks.
I don't think there is an "official" way to force a client side reload when you deploy new code. Usually this should not be a problem, because when the client calls the app, it caches the JS and CSS files, so a deploy should not have any effects on the version of the application a client is currently running...
But if this really is a problem, you could provide your application version via an HTTP API, have your angular app check it on every interaction, and reload the page if the version has changed.
version.txt
1.0.1
src/environments/environment.prod.ts
export const environment = {
production: true,
version: '1.0.2'
};
src/app/version.service.ts
import {Injectable} from '#angular/core';
import {HttpClient} from '#angular/common/http';
import {environment} from '../environments/environment';
#Injectable()
export class VersionService {
constructor(private __httpClient: HttpClient) { }
checkVersion() {
this.__httpClient.get('/version.txt').subscribe(data => {
if (data != environment.version) {
alert('Code is outdated, website will reload');
window.reload();
}
}
}
}
Add a constructor to all your components and check the version
src/app/app.component.ts
constructor(private __versionService: VersionService) {
this.__versionService.checkVersion();
}
Note: this code is completely untested :-) You might have to tinker with it... Also, I am not sure if this actually IS the best way to do it, but I couldn't find a better answer anywhere either.
Thank you so much #masterfloda!!
I been working in your aproach, and it worked like a charm, I did some tunning to your code so I hope you don't mind if I publish the updated code to help other people facing the same problem.
version.txt
1.0
I noticed that when it was more than one point betwen numbers (0.0.0 -> 0.0) it fails comparing the values.
I didn't use the src/environments/environment.prod.ts aproach because I wanted a way to update the value of version inside src/environments/environment.prod.ts and was not sure how to do it once in production, so I stored the value in local storage.
src/app/version.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Compiler } from '#angular/core';
import { GlobalVariablesService } from '../services/global.service';
//https://stackoverflow.com/questions/47440576/how-to-handle-angular-2-code-updates
#Injectable()
export class VersionService {
constructor(private __httpClient: HttpClient,
private _compiler: Compiler,
public variablesService: GlobalVariablesService
) { }
checkVersion() {
this.__httpClient.get('https://host.com/version.txt?'+Math.round(Math.random() * 10000),{responseType: 'text'}).subscribe(
response => {
let text_version = response;
let stored_version = this.variablesService.getVersion();
//alert('text_version: '+text_version);
if(stored_version == undefined){
this.variablesService.setVersion(text_version);
} else if (+text_version != +stored_version) {
this.reloadAndStore(text_version);
}
},
error => {
console.log(<any>error);
}
);
}
reloadAndStore(text_version){
//alert('Code is outdated, website will reload');
this._compiler.clearCache();
this.variablesService.setVersion(text_version);
location.reload();
}
}
version.txt?'+Math.round(Math.random() * 10000)
Here you will see I'm using a random param, because if not I noticed when the web app is installed in ios homescreen it catches even that version.text file.
../services/global.service
...
getVersion() {
if(this.deCodeLocal('storedVersion') === null){
return undefined;
} else {
return this.deCodeLocal('storedVersion');
}
}
setVersion(val) {
this.enCodeLocal('storedVersion', val);
}
...
src/app/app.component.ts
constructor(private __versionService: VersionService) {
this.__versionService.checkVersion();
}
I hope it helps somebody, thank so much.
I've been stuck on an error that I'm not completely sure how to solve.
My application is made in Angular2 and runs completely in a webworker largely based on this tutorial http://www.syntaxsuccess.com/viewarticle/web-workers-in-angular-2.0
My first feature was an implementation of socket.io which is working perfectly(also with observables etc..) but now I want to use the Http service of Angular2 and I get the following error:
My code of the service is like this and the error arrises when I call validateAccessToken (I have to add the .js on my imports otherwise I get a 404 on the files within the webworker):
import { Injectable } from '#angular/core';
import { Http, Headers, RequestOptions, Response } from "#angular/http";
import { environment } from "../../../environments/environment.js";
import { Observable } from "rxjs/Observable.js";
import 'rxjs/add/operator/toPromise.js';
import 'rxjs/add/operator/map.js';
#Injectable()
export class AuthService {
headers: Headers;
options: RequestOptions;
url: string;
constructor(private http:Http) {
this.url = environment.authServerUrl;
}
validateAccessToken(token) {
return this.http.get(this.url)
.map(this.extractData)
.catch(this.handleError);
};
extractData(response: Response) {...}
handleError(error: any) {...}
}
I know the question is quite vague but with the information I get from the error it's not really clear what's going wrong for me.
The CookieXSRFStrategy is default enabled by Angular2 and used by http.
The webworker does not have DOM access to get the cookie to insert in the http headers. And thus throws the error Uncaught not implemented.
You should implement your own CookieXSRFStrategy strategy which at least does not throw this error ;)
I am working on the front end of a file upload service. I am currently ignoring the service path with respect to the backend. I have run into a strange problem. I have a few generated components that sit within the app component. When I end the serve from console and do ng serve again, it errors out. It says:
The only way I have found to get rid of this is to erase my uploader service injection, save the file, then re-insert the injection. This is how it is supposed to look:
The only way to get ng serve to work is to by erasing the line private service: UploaderService
Any idea why this is happening? Am I missing something with my injection? My UploaderService is marked as Injectable() and the components that use it are under Directives.
Update:
What I found out is that it is unrelated to the UploaderService. I have a component that does not inject the UploaderService. I fix it the same way I fix the other components that inject the UploaderService. By deleting the parameters of the constructor, saving, and then putting the parameters back. Then it will serve
Update2:
The generated componenet, upload.component.t, has a spec file that is generated with it, upload.component.spec.ts
It has a error that asks for parameters like so:
My UploadComponent constructor has a parameter in it, where i inject the UploaderService. In the spec.ts file, a new UploadCompent is created, but does not contain any arguments. I am guessing this is where I am going wrong. How do I work around this?
Here is my UploaderService:
import { Injectable } from '#angular/core';
import {Http, Response, HTTP_PROVIDERS, Headers, HTTP_BINDINGS, RequestOptions} from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/Rx';
import { ItemEntryComponent } from './item-entry';
import { Query } from './Query';
#Injectable()
export class UploaderService {
public URL: string;
private query: Query;
public filesSelected: Array<ItemEntryComponent> = [];
progress$: any;
progress: any;
progressObserver: any;
//CONSTRUCTOR
constructor(private http: Http) {
//***SET URL***
this.URL = 'http://localhost:7547/api/picker';
//Create Query for url
this.query = new Query(this.URL);
//Create progress attribute
this.progress$ = Observable.create(observer => {
this.progressObserver = observer
}).share();
}
}
Problem solved!
I had not realized the generated files included a spec testing file, in my example it was upload.component.spec.ts. Getting rid of those files gets rid of the errors that ask for parameters to be filled in inside the test files and now ng serve works.