I have a web api call. The property of checkNumber is a double on the web api side , however in my typescript model I need it to come in as a string. It is staying as a number even though my model clearly has it as a string variable.
Is there a way to get the conversion to automatically happen to string?
my web api call
public GetMyClass(myModel: MyClass): Observable<MyClass> {
let headers = new HttpHeaders();
headers.append("content-type", "application/json");
headers.append("accept", "application/json");
let options = { headers: headers };
return this.httpClient.post<MyClass>( url, myModel, options)
}
my model
export MyClass{
checkNumber?: string;
}
Typescript doesn't do auto conversion. It helps with type checking during development. At runtime, it just plain javascript.
You will need to define your own conversion.
public GetMyClass(myModel: MyClass): Observable<MyClass> {
let headers = new HttpHeaders();
headers.append("content-type", "application/json");
headers.append("accept", "application/json");
let options = { headers: headers };
return this.httpClient.post<MyClass>( url, myModel, options)
.pipe(
map(dataObject => {
let checkNumber = dataObject.checkNumber
return {
checkNumber: checkNumber ? dataObject.checkNumber.toString() : undefined,
...dataObject
}
})
)
}
Related
I am using angular 15.0. I get a list of items from backend (asp.net core 5) with an item appended to the header. the get method in the client side service is:
/** GET Paged commodities from the server ===============================*/
getPagedCommodities(pageSize: number, pageNumber: number): Observable<CommodityForList[]> {
let params: HttpParams = new HttpParams();
params = params.append('pageSize', pageSize);
params = params.append('pageNumber', pageNumber);
const headers = new HttpHeaders({
observe: 'response'
});
return this.http.get<CommodityForList[]>(this.baseUrl + '/getPagedCommodities/', { headers, params });
}
this method sends two parameters to the server and gets commodities list along with totalCount to paging the list. I need the totalCount to set the length property of mat-paginator and commodities list as observable to provide a dynamic search for user. therefore, in the commoditiesList component, the snippet code for this purpose is:
commoditiesForList$!: Observable<CommodityForList[]>;
this.commoditiesForList$ = this.commoditiesService.getPagedCommodities(this.pageSize, this.pageIndex+1);
this.commoditiesForList$.subscribe( res => {
const totalCount = res.headers.get('X-Pagination');
})
but, here I have an error: Property 'headers' does not exist on type 'CommodityForList[]'.
when I change the type of commoditiesForList$ to HttpResponse<CommodityForList[]>, the error may be fixed, but receiving the commodities list as observable will have a problem. Is there a solution to get the commodities list as observable and read the totalCount separately from the header? thank you for your response.
See https://angular.io/guide/http#reading-the-full-response
You might need more information about the transaction than is contained in the response body. Sometimes servers return special headers or status codes to indicate certain conditions that are important to the application workflow.
getPagedCommodities(pageSize: number, pageNumber: number): Observable<HttpResponse<CommodityForList[]>> {
// ...
return this.http.get<CommodityForList[]>(this.baseUrl + '/getPagedCommodities/', { headers, params });
}
You can access the body in the body attribute.
See maybe also Angular: HttpClient read full response with Observable Array.
Given your example, you could use the next configuration on your HTTP request: {responseType: 'json', observe: 'events' }. See a working example here on stackblitz - one request
commoditiesForList$!: BehaviourSubject<CommodityForList[]>;
totalCount$!: BehaviourSubject<any>;
constructor(commoditiesService: CommoditiesService) {
this.commoditiesService.getPagedCommodities(this.pageSize, this.pageIndex+1).subscribe(res => {
this.commoditiesForList$.next(res.body)
this.totalCount$.next(headers.get('X-Pagination'))
})
}
Original Answer
Given your example, you could use the next configuration on your HTTP request: {responseType: 'json', observe: 'events' }. See a working example here on stackblitz - two requests - shared pipe
Edit: to avoid making two request, notice that GET request is using share operator from rxjs. Thanks Arber to notice it.
getPagedCommodities(pageSize: number, pageNumber: number): Observable<CommodityForList[]> {
let params: HttpParams = new HttpParams();
params = params.append('pageSize', pageSize);
params = params.append('pageNumber', pageNumber);
const headers = new HttpHeaders({
observe: 'response'
});
return this.http.get<CommodityForList[]>(this.baseUrl + '/getPagedCommodities/',
{ headers, params, responseType: 'json',observe: 'events'}).pipe(share());
}
then you will access data and headers in this way
commoditiesForList$!: Observable<CommodityForList[]>;
this.commoditiesForList$ = this.commoditiesService.getPagedCommodities(this.pageSize, this.pageIndex+1).pipe(
map((res) => (res as any).body));
this.totalCount$ = this.commoditiesService.getPagedCommodities(this.pageSize, this.pageIndex+1).pipe(
map((res) => (res as any).headers)), map(headers => headers.get('X-Pagination')));
Docs: https://angular.io/guide/http#requesting-data-from-a-server
I am trying to externalize the url and proerties in angular 6.
Have a service which invokes 3rd party URL to get data.
weather-component.html -> weather.component.ts -> weather.service.ts
In my weather.service.ts,
public getWeather() {
// Here I have hardoded the URL and the api key.
}
I would like to externalize this so as to make it configurable.
Not sure how to move it to a configurable file and read from there.
Exactly the same manish's answer but when we started the project in Angular 2 , this blog had been quite helpful. Over the upgrades over http gateway has changed massively and it was very useful when angular changed the httpclient in 4 .
https://blog.sstorie.com/adapting-ben-nadels-apigateway-to-pure-typescript
Also if you are looking for a place to put base url etc , I would use the environment ts file in angular-cli
https://github.com/angular/angular-cli/wiki/stories-application-environments
I suppose you want to make generic service
you can have a baseHttp.service.ts and weather.service.ts will extend the baseHttpservice to make the api calls.
baseHttp.service.ts
#Injectable()
export abstract class BaseHttpService {
private baseUrl: string = Constants.BASEURL;
protected method: string;
protected serviceUrl: string;
protected headers: Headers;
constructor(private http: Http) {
this.headers = new Headers();
this.headers.append('Content-Type', 'application/json');
this.headers.append('Accept', 'application/json');
}
call(params: any) {
let url: string = this.setUrl();
let options = new RequestOptions({ headers: this.headers });
options.url = url;
options.method = this.method;
options.body = params;
return this.http.request(url, options).toPromise()
.then((response: any) => this.extractData(response))
.catch((error: any) => this.handleError(error));
}
//complete the other functions
}
weather.service.ts
#Injectable()
export class DashboardService extends BaseHttpService {
constructor(private _http: Http) {
super(_http);
}
getWeatherReport(params:any) {
this.serviceUrl = 'some-url';
this.method = "GET";
return super.call(params);
}
}
so you can inject weather.service.ts and override the values in weather.service.ts and make http calls
so baseHttp.service.ts acts as a interceptor, so you can intercept all the Http calls there.
Angular 5.0.1
I'm looking at the docs for Angular HttpClient: https://angular.io/guide/http,
but I can't seem to figure how to send POST params as a URLEncoded string instead of a JSON string. For instance, my Java http clients will send like this as default:
username=test%40test.com&password=Password1&rolename=Admin
But Angular wants to send as Json by default:
{"username":"test#test.com","password":"Password1","rolename":"Admin"}
Here's my code currently:
let body = {
username: "test#test.com",
password: "Password1",
rolename: "Admin"
};
let headers = new HttpHeaders();
headers = headers.set("Content-Type", "application/x-www-form-urlencoded");
this.http.post(this.baseUrl, body, {
headers: headers,
})
.subscribe(resp => {
console.log("response %o, ", resp);
});
I've also tried adding HttpParams:
let httpParams = new HttpParams();
httpParams.append("username", "test#test.com");
httpParams.append("password", "Password1");
httpParams.append("rolename", "Admin");
...
headers: headers,
params: httpParams
But HttpParams seem to have no effect.
Any idea how to URL encode the request instead of Json?
append() returns a new HttpParams object, so you'll need to make a slight modification to your httpParams code. Try this:
let httpParams = new HttpParams()
.append("username", "test#test.com")
.append("password", "Password1")
.append("rolename", "Admin");
In the code above, we chain our append calls, creating a new HttpParams object on each call. The last time we call append, the HttpParams object returned will contain all of the previously appended parameters.
That is because HttpParam is immutable.
You can read why here
In short:
let httpParams = new HttpParams()
.append("username", "test#test.com")
.append("password", "Password1")
.append("rolename", "Admin");
Because the app may retry requests, the interceptor chain may process
an individual request multiple times. If requests were mutable, a
retried request would be different than the original request.
Immutability ensures the interceptors see the same request for each
try.
Supply the HttpParams object as the body argument for post(). That way, you will send form data as the request body instead of JSON. Also, the params option is not needed.
Here's an example:
const body = new HttpParams()
.set('username', 'test#test.com')
.set('password', 'Password1')
.set('rolename', 'Admin');
this.httpClient.post(url, body, {
headers: new HttpHeaders()
.set('Content-Type', 'application/x-www-form-urlencoded')
});
The HttpParams class is immutable. All mutation operations will return a new instance. You'll need to chain the set() calls to preserve the previously added parameters. You can use both set() and append().
Alright, simple basic auth authentication service in angular2.
When a user logins first time, it works. But when he/she tries to login second time with a different account. I got double basic auth string in the request headers, it's like "Authorization:Basic YWRtaW46YWJjMTIz,Basic RMcasd9WJjMXoPj".
This is the service:
#Injectable()
export class AuthenticationService {
private url: string = 'http://localhost:8080/api/test';
private username: string;
private password: string;
private authenticationStatus: boolean = false;
constructor(private http: Http) { }
authentication(username: string, password: string): Promise<boolean> {
let headers = new Headers(); // <== previous headers object with old Authorization string get back from grave.
console.log(headers);
headers.append("Authorization", "Basic " + btoa(username + ":" + password));
headers.append("Content-Type","application/json;charset=utf-8");
return this.http.get(this.url, { headers: headers })
.toPromise()
.then(response => { ...
This is my first try angular/typescript app. I'm confused by not getting a brand new object when I use both let and new here. Is it because headersString within the header class is static? I did look into the angular headers class api doc. I tried call headers.delete("Authorization"); right after the let headers = new Headers();, The old Authorization header remains.
I'm confused by not getting a brand new object when I use both let and new here.
When you log an object the reference to the object is logged.
As an example:
let x = {};
console.log(foo);
x.foo = 'foo';
Now on your console if you look at x after you've mutated it, it will show the new stuff:
In short. Your code is correct. The console can lie to you.
Alternative answer: You are doing something that is not demonstrated in the code sample you have provided.
For a web application I need to get my images with an ajax request because we have signature + authentication on our API, so we can't get images using a simple <img src="myapi/example/145"/>
Since we're using angular2, we obviously looked for blob or something like that, but as stated in static_response.d.ts file:
/**
* Not yet implemented
*/
blob(): any;
So okay, I can't do it for now, I have to wait for thie feature to be implemented.
But problem is I can't wait so I need a hotfix or a little hack to be able to get image data from response and I'll be able to remove my hack and set the blob() method call to be good when it will be implemented.
I tried this:
export class AppComponent {
constructor(private api:ApiService, private logger:Logger){}
title = 'Tests api';
src='http://placekitten.com/500/200'; //this is src attribute of my test image
onClick(){ //Called when I click on "test" button
this.api.test().then(res => {
console.log(res._body);
var blob = new Blob([new Uint8Array(res._body)],{
type: res.headers.get("Content-Type")
});
var urlCreator = window.URL;
this.src = urlCreator.createObjectURL(blob);
});
}
}
with ApiService.test() method:
test():Promise<any> {
return this.http.get(this._baseUrl + "myapi/example/145", this.getOptions())
//getOptions() is just creating three custom headers for
//authentication and CSRF protection using signature
.toPromise()
.then(res => {
this.logger.debug(res);
if(res.headers.get("Content-Type").startsWith("image/")){
return res;
}
return res.json();
})
.catch(res => {
this.logger.error(res);
return res.json();
} );
}
But I don't get any image from it and logging the response data shows a big string which is image data.
Do you have a hack to achieve this?
It is not necessary to extend BrowserXhr anymore. (Tested with angular 2.2.1)
RequestOptionsArgs now has a property responseType: ResponseContentType which can be set to ResponseContentType.Blob
Using DomSanitizer
import {DomSanitizer} from '#angular/platform-browser';
This example also creates a sanitized url that can be bound to the src property of an <img>
this.http.get(url, {
headers: {'Content-Type': 'image/jpg'},
responseType: ResponseContentType.Blob
})
.map(res => {
return new Blob([res._body], {
type: res.headers.get("Content-Type")
});
})
.map(blob => {
var urlCreator = window.URL;
return this.sanitizer.bypassSecurityTrustUrl(urlCreator.createObjectURL(blob));
})
Using the new Angular HttpClient is really easy to achieve this. Going off of tschuege's approach, it would be:
return this._http.get('/api/images/' + _id, {responseType: 'blob'}).map(blob => {
var urlCreator = window.URL;
return this._sanitizer.bypassSecurityTrustUrl(urlCreator.createObjectURL(blob));
})
The key is to set the responseType as 'blob' so that it doesn't attempt to parse it as JSON
I think that you missed to set the responseType on your request. Right now it's a bit tricky because it's not supported.
The workaround would be to override the BrowserXhr class to set the responseType on the xhr object itself...
You could extend the BrowserXhr:
#Injectable()
export class CustomBrowserXhr extends BrowserXhr {
constructor() {}
build(): any {
let xhr = super.build();
xhr.responseType = 'arraybuffer';
return <any>(xhr);
}
}
and override the BrowserXhr provider with the extended class:
bootstrap(AppComponent, [
HTTP_PROVIDERS,
provide(BrowserXhr, { useClass: CustomBrowserXhr })
]);
The problem is here that you don't override for all requests. At the bootstrap level, it will override everything. So you could provide it in a sub injector within the providers attribute of the impacted component...
Here is a working plunkr: https://plnkr.co/edit/tC8xD16zwZ1UoEojebkm?p=preview.
This JSFiddle could help you:
https://jsfiddle.net/virginieLGB/yy7Zs/936/
The method is, as you wanted, creating a Blob from the URL provided
// Image returned should be an ArrayBuffer.
var xhr = new XMLHttpRequest();
xhr.open( "GET", "https://placekitten.com/500/200", true );
// Ask for the result as an ArrayBuffer.
xhr.responseType = "arraybuffer";
xhr.onload = function( e ) {
// Obtain a blob: URL for the image data.
var arrayBufferView = new Uint8Array( this.response );
var blob = new Blob( [ arrayBufferView ], { type: "image/jpeg" } );
var urlCreator = window.URL || window.webkitURL;
var imageUrl = urlCreator.createObjectURL( blob );
var img = document.querySelector( "#photo" );
img.src = imageUrl;
};
xhr.send();