Hello fellow Programmer,
we have an component which loads after clicking on a link, this components content depends on the the link its got clicked. For Example we click on the Link and load a JSON from an API which contain the Data, this Data is shown on our HTML template.
So far we have an succesfull API call which gets us the JSON and we bind it on an var which is conected to the HTML by {{var}}, but it wont display the JSON at all.
We are pretty sure it is a problem with the asynchron call from the API to get the Data, but we have no idea how to fix this.
component.service.ts with the getVoucher() method
getVoucher() {
let voucherUrl = 'xxx'; // URL to web api
let headers = new Headers();
headers.append('Content-Type', 'application/json;charset=UTF-8');
headers.append('Authorization', 'Basic '+btoa("xxx"));
let options = new RequestOptions({ headers: headers });
return this.http.get(voucherUrl,options).map(response => response.json());
}
component.ts
private gutschein;
private strGutschein;
ngOnInit(): void {
this.voucherService.getVoucher().subscribe(data => {
this.gutschein = data;
console.log(this.gutschein);
});
setTimeout(() => console.log(this.gutschein), 2000);
//console.log(this.gutschein);
this.strGutschein = JSON.stringify(this.gutschein);
}
and the HTML Part component.html
{{strGutschein}}
your component code should be like this
private gutschein;
private strGutschein;
ngOnInit(): void {
this.voucherService.getVoucher().subscribe(data => {
this.gutschein = data;
console.log(this.gutschein);
this.strGutschein = JSON.stringify(this.gutschein);
console.log(this.strGutschein);
});
setTimeout(() => console.log(this.gutschein), 2000);
}
and in html part use
{{ strGutschein | json }}
Related
I do a http post with a header. After this Post I navigate to a List of Customers and want to show SuccesMessage that Entry is included. How can I display message on other route?
Here is my Code:
postData({
let url = "api.url"
var header = new HttpHeaders .... // etc.
this.http.post(url, { company: this.form.value.company }, { headers (header)}).toPromise().then((data: any) => { console.group(data) })
After post route to page an show message:
this.router.navigate(['customer']);
this.showMsg = true;
Here is my html
<div *ngIf="showMsg">Success</div>
But no success message is shown. Why? And what do I have to do? Thank you so much for help!
You need to use a data sharing entity such as a service. For example you could have a service containing this:
private msgSubject: BehaviorSubject<void> = new BehaviorSubject<void>(void 0);
public msg$: Observable<void> = this.msgSubject.asObservable();
showMsg(): void {
this.msgSubject.next();
}
then in the component you navigate to after redirection you just have to inject the service and then subscribe to the observable msg$:
this.msgService.msg$.subscribe(() => (this.showMsg = true));
You will also need to call the showMsg method of the service once your post is successful.
I am trying to map a Http JSON Response to a Custom Interface in Angular / typescript. I have tried it in several ways but have not made it yet. The JSON object is not correctly mapped to the interface. The map attribute stays "undefined". If I print the data directly, the JSON data is output correctly - the problem is that I don't know how to access it. Here is my code:
export interface IMap<T> {
map: Map<string, Array<T>>;
}
The JSON answer looks like this. It is Map< String,List< ? >> in Java.
{
"somenumbers": [
20,
40
],
"somemorenumbers": [
71,
111
]
}
Now I tried to map it the following way:
public getValues(
paramList: Array<string>
): Observable<IMap<any>> {
const url = `url`;
let params = new HttpParams();
for (let s of paramList) {
params = params.append("params", s);
}
return this.http.get<IMap<any>>(url, { params });
}
In the configservice I subscribe to the Method. How do I map the Response correctly so that the attribute map in data isn't undefined and can be accessed correctly?
this.configService
.getValues(["somenumbers", "somemorenumbers"])
.subscribe((data: IMap<any>) => {
//outputs the JSON Data as Object{somenumbers: Array(2), somemorenumbers: Array(2), map: Map(0)}
console.error(data);
console.error(data.map);//map is undefined => ERROR
});
As you can see the map attribute is undefined. It is just a "map: Map(0)". Now... - How do I get the JSON stuff into the export interface? The map attribute should be filled with the associated values.
I appreciate any help! :)
If I understood correctly you're expecting that by adding <IMap<any>> to the get call it will then return you the response mapped to IMap. It doesn't, check this issue.
What you can do instead is use rxjs map to map the response yourself like so:
return this.http.get<IMap<any>>(url, { params }).pipe(
map((response) => {
// map the response here
})
);
I realized that I actually don't need the export interface and changed the code to the following. It took a while for me to get that x.y is in ts the same as x["y"]. Via response[parameter] I can access the attributes of the response Object dynamically - exactly what I needed.
public getValues(
paramList: Array<string>
): Observable<Map<string, Array<any>>> {
const url = `url`;
let params = new HttpParams();
for (let s of paramList) {
params = params.append("params", s);
}
return this.http
.get<any>(url, {
params
})
.pipe(
map(response => {
let toReturn = new Map<string, any[]>();
for (let parameter of paramList) {
toReturn.set(parameter, response[parameter]);
}
return toReturn;
})
);
}
The mapping works now! The JSON answer is still the same as in the question above.
this.configService
.getValues(["somenumbers", "somemorenumbers"])
.subscribe((data: Map<string, any[]>) => {
console.error(data);
});
Thanks for the help and links #M Mansour !
I created a dummy website for a 'game library' just to teach myself the http POST, GET, PUT and DELETE methods, I have a separate file for my api with 3 'games' in. The post does work when I enter a new game name and click enter nothing happens but when I refresh the page my html table then displays the first three games plus the one I just posted. I would like to have it post and then display it on the table right away rather than having to refresh the page as its abit sloppy in my opinion.
I have tried putting the input box above the table, but I don't really know what else to try.
<h3>Here is your list of current games in your library</h3>
<table>
<tr>
<th>Id</th>
<th>Name</th>
</tr>
<tr *ngFor="let game of games">
<td>{{game.id}}</td>
<td>{{game.name}}</td>
</tr>
</table>
public games: any = [];
private url = 'http://localhost:3000/api/games';
constructor(private httpClient: HttpClient){
httpClient.get(this.url).subscribe(response => {
this.games = response;
});
}
createGame(name: HTMLInputElement) {
let post = {name : name.value};
name.value = '';
let headers = new HttpHeaders();
headers= headers.set('Content-Type', 'application/json');
this.httpClient.post(this.url, {id: this.games.length + 1,name: post.name }, {headers})
.subscribe(response => {
console.log(response)
})
}
I would like the table to auto update when I post a new game to it.
You have to implements OnInit on your component, see the doc here [https://angular.io/api/core/OnInit]
Your get request should be in the ngOnInit() like this :
ngOnInit(){
httpClient.get(this.url).subscribe(response => {
this.games = response;
});
}
After creating your new data, call ngOnInit() function and
your table will be updated.
You are not retrieving back the changed data. Once the POST call is successful, make a GET call to get the updated data.
createGame(name: HTMLInputElement) {
let post = {name : name.value};
name.value = '';
let headers = new HttpHeaders();
headers= headers.set('Content-Type', 'application/json');
this.httpClient.post(this.url, {id: this.games.length + 1,name: post.name },{headers}).subscribe(response => {
console.log(response)
// Subscribe back the changes
this.httpClient.get(this.url).subscribe(response => {
this.games = response;
});
})
}
Another way is to write another function which uses the GET call to update the data. You can use that one for initially loading the data as well as updating after new game creation.
#sammyleighg try like this,
you can update your games list once you have successfully posting your data, just make another http call to your Server.
create a method to simplified logic.
Component.ts
public games: any = [];
private url = 'http://localhost:3000/api/games';
constructor(private httpClient: HttpClient){
this.getGamesInfo();
}
getGamesInfo() {
this.httpClient.get(this.url).subscribe(response => {
this.games = response;
});
}
createGame(name: HTMLInputElement) {
let post = {name : name.value};
name.value = '';
let headers = new HttpHeaders();
headers= headers.set('Content-Type', 'application/json');
this.httpClient.post(this.url, {id: this.games.length + 1,name: post.name }, {headers})
.subscribe((response: any) => {
if(response.status == 200) {
this.getGamesInfo();
}
})
}
I will recommend to use ngOnInit() for making Api calls rather than in constructor()
You have to add newly created game to games array inside createGame method:
this.games.push({id: this.games.length + 1,name: post.name });
For PUT method you need to find edited item in array and then change its data:
let editedGame = this.games.find(game => game.id === UserInputId);
editedGame.name = UserInputName;
I realized a strange thing with my Angular2 typescript project. I have objects coming in from a webservice which have the type "Level" (it has the same properties as the Json coming from the webservice). In runtime comes out that the properties of the Level from the webservice have capital letters (Pascal case) at the beginning and the ones in my typescript project have small ones (visible in the browser's developer debug tool).
I guess I need to map the json properties somewhere somehow instead of doing a cast by writing "as Level[]" everywhere. How to I do it properly?
Update regarding the question that I should post some code:
(Controller)
ngOnInit(): void {
this.levelsObservable = this.levelsService.getAllLevels();
this.levelsObservable.subscribe(
data => console.log(data)
);
}
(Service)
observable : Observable<Response>;
getAllLevels(): Observable<Level[]> {
this.observable = this.achievementsService.getAllAchievements(this.allLevelsUrlPart);
return this.observable
.map((response: Response) => {
const srcData = response.json() as Level[];
return srcData;})
.catch(error => this.handleError(error));}
getAllAchievements(detailPath): Observable<Response> {
// prepare request url and header
this.specificUrl = this.webServiceUrl + detailPath;
this.headers.append('Content-type', 'application/json');
let options = new RequestOptions({ headers: this.headers });
this.result = this.http.get(this.specificUrl, options)
.catch(error => this.handleError(error));
return this.result;}
Update:
I polished my code a bit with the help of one answer below (not integrated above because not essential to solve the main problem).
I tried to use the other answer from below to reach the camel cases but it wasn't working (I have an array and in the array are objects with properties, but an object's properties aren't accessible with iterator methods).
Update:
I finally managed it (!) :) I shortened this post a bit and will now post my solution below. It's for sure not the most beautiful, but I'm happy to have one after searching around for hours. Thanks to all people helping me with their great and input here!
You could use this to get the lowercased objects.
modifiedSrc(srcData){
let obj = {};
Object.keys(srcData).forEach((key)=>{
obj[key.uncapitalize()] = srcData[key];
})
return obj
}
String.prototype.uncapitalize = function() {
return this.charAt(0).toLowerCase() + this.slice(1);
}
Then you can return the modified data
getAllLevels(): Observable<Level[]> {
this.observable = this.achievementsService.getAllAchievements(this.allLevelsUrlPart);
return this.observable
.map((response: Response) => {
const srcData = response.json() as Level[];
return this.modifiedSrc(srcData);})
.catch(error => this.handleError(error));}
You have complicated both of your methods.Make it simple as
this.webServiceUrl = "http...." ; // your service end point address
this.headers.append('Content-type', 'application/json');
let options = new RequestOptions({ headers: this.headers });
// For all your error handling
private handleError(error: Response) {
console.log(error);
return Observable.throw(error.json().error || 'Internal Server error');
}
Your service method can use TypeCasting which will look like
getAllLevels(detailPath): Observable<Level[]> {
return this.http.get(detailPath, options)
.map((response: Response) => <Level[]>response.json())
.catch(this.handleError);
}
Your component should raise the request to your service as
ngOnInit() : void{
this._myService.getAllLevels()
.subscribe(levels => this.levels = levels,
error =>this.errorMessage =<any> error);
}
Your variable declaration must be like
levels:Level[];
So finally... I got a solution! For sure not the most beautiful one, but easy to understand and done with hard work and research:
private useLevelProperties (response: any): Level[]{
let levels: Level[] = [];
Object.keys(response).forEach((key) => {
//create a new object and just take out the json parts needed. The webservice retrieves Pascal case letters, so we
//need to convert them into camel case ones.
this.level = new Level(response[key]["AchievementId"], response[key]["Image"],
response[key]["GrantedTo"], response[key]["GrantedBy"], response[key]["GrantedWhen"], response[key]["Description"],
response[key]["Name"], response[key]["CompetitionCode"], response[key]["Number"]);
levels[key] = this.level;
});
return levels;
};
Another option is to do it server-side with an extra json option for camel case: example for server-side camel casing
I'm new to Angular2 and somehow it's really hard to me to understand how http works in Angular2. I made a simple component which should display a json response. It doesn't work and I have no idea why. I checked many tutorials and tried it with promises as well as observables. Doesn't work. I just can't get the data of the response.
My code:
private doAction() {
this.doHttpRequest().subscribe(
data => this.content = data
);
this.content = JSON.stringify(this.content);
}
private doHttpRequest() {
return this.http.get('http://jsonplaceholder.typicode.com/posts/1')
.catch(this.handleError);
}
this.content is bind to my template. When I click a button to start doAction() for a second I see "" in the template, after another second [object Object]
What is the problem here?
That's the expected behavior
private doAction() {
// schedule HTTP call to server and subscribe to get notified when data arrives
this.doHttpRequest().subscribe(
// gets called by the observable when the response from the server aarives
data => this.content = data
);
// execute immediately before the call to the server was even sent
this.content = JSON.stringify(this.content);
}
To fix it change it to
private doAction() {
this.doHttpRequest().subscribe(
data => {
//this.content = data;
this.content = data.json());
});
);
}
If you want code to be executed after data arrived, then you need to move it inside the subscribe(...) callback.
Since http requests are asynchron you have to put all your logic depending on the results of the http call in the subscribe() callback like this:
private doAction() {
this.doHttpRequest().subscribe(
data => {
this.content = data;
// any more logic must sit in here
}
);
}
private doHttpRequest() {
return this.http.get('http://jsonplaceholder.typicode.com/posts/1')
.map(res => res.json());
.catch(this.handleError);
}
Http call is returning data since it shows "[object Object]" in template. If you want to see the json data in template you can use the json pipe as below.
{{content | json}}
PS: No need of "this.content = JSON.stringify(this.content);" in your code.