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
Related
I'm experimenting with reactjs and .net core web api, and I'm trying to send simple list of integer to the rest api. Throught postman I did it without any issues, but when I try to do it from my react js code I'm getting 415 error in a console:
This is how my method on the API looks:
// DELETE api/SiteUsers/delete
[HttpPost("delete")]
public async Task<ActionResult> DeleteSiteUsers(DeleteValuesDto deleteValues, CancellationToken cancellationToken)
{
return NoContent();
}
Here is how my DeleteValuesDto looks like:
On the frontend I made simple method which should make an api call to rest api:
const handleDelete = async () => {
var deleteValues = {
values: selectedRows,
};
await deleteSiteUser(deleteValues);
};
Here is my deleteSiteUser method on frontend:
export const deleteSiteUser = (deleteValues) => {
console.log('deleteValues', deleteValues);
return axiosWrapper.request({
url: `/SiteUsers/delete`,
method: 'POST',
deleteValues,
});
};
Here are the results of console log:
I thought this would be very simple, but I find out it isn't actually :/
You need to send the data in the named data property as per the documentation (https://github.com/axios/axios#request-config)
export const deleteSiteUser = (deleteValues) => {
console.log('deleteValues', deleteValues);
return axiosWrapper.request({
url: '/SiteUsers/delete',
method: 'POST',
data: deleteValues,
});
};
If you are not setting the data property, the payload is empty. The most likely outcome of this is, that the Conent-Type header is not set and thus Asp.Net cannot decide how to parse the request body and then throws that error. You could just check that in your developer console in Chrome, then set the data property and check the Content-Type header for that reqeust as well.
You're wrapping the IDs in an object that has a values field, but your endpoint is expecting a plain array.
Instead of wrapping selectedRows like you do with deleteValues, just pass it through directly.
const handleDelete = async () => {
await deleteSiteUser(selectedRows);
};
Featherjs find service unable to pass extra parameters through find function. In below find service passing extra params data to service.
but unable to receive the value at service hook.
Client code :
return this.app.service('userlist').find({
query: { usersIds: { "$in" : [this.user._id]} },
paginate: false,
params:{ name:'sam' }
}).then(response => {
}).catch(error => {
console.log(error);
});
Server code (Service hook ) :
module.exports = function (options = {}) {
return async function dogetUsr (context) {
const { data } = context;
console.log('Client Param data -->',context.params.name);
return context;
};
};
params data not receiving at server -->
params:{ name:'sam' }
Output at server/service hook :
Client Param data -->undefined
For security reasons, only params.query is passed between the client and the server. In general I wouldn't recommend letting the client disable pagination unless you are guaranteed to only get a few (less than 100) records. Otherwise requests with many records can cause major issues on both sides.
If it is still something you need, you can use the disablePagination hook which lets you set the $limit to -1 if you want to disable pagination:
const { disablePagination } = require('feathers-hooks-common');
module.exports = { before: {
find: disablePagination()
} };
Anyone had experience with an afterware that modifies the response data?
const parseResponse = new ApolloLink((operation, forward) => {
return forward(operation).map((response) => {
response.data.parsed = transformData(response.data)
return response
})
})
const link = parseResponse.concat(networkLink)
This works great on websockets events - the data is transformed, added to this parsed field in the data response.data, but on a regular <Query... request, the parsed field is deleted so the component can't read it. I've confirmed that this method is called correctly in query requests and that the parsed field is added as well, but somewhere between the afterware and the component the parsed field gets stripped
Apollo Client v3 introduced a feature for customizing the behavior of cached fields:
You can customize how individual fields in the Apollo Client cache are read and written. To do so, you define a FieldPolicy object for a given field. You nest a FieldPolicy object within whatever TypePolicy object corresponds to the type that contains the field.
Here's how to parse date strings into Date objects:
export const apolloClient = new ApolloClient({
link: ...,
cache: new InMemoryCache({
typePolicies: {
Post: {
fields: {
updatedAt: {
read(time: string) {
return new Date(time);
},
},
},
},
},
}),
});
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.