Receiving JSON-Data via http: empty response - json

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.

Related

Angular 6 HttpClient.get Observable does not assign value

I suppose that the answer will be very obvious, but still it evades me. I'm new on working with observables, and now I'm facing issues assigning a value from one. I had success if I define it (this._apps) as an Observable and asking from the view to the service using subscribe (But for my taste is was way convoluted (three levels inside a map just to return another observable with the array and then another function to subscribe the previous to assign the variable and another subscription in the view to finally show the information), inefficient and on top of that I could not get it "right" again). The task is very simple. Given the class Application
export class Application {
name: string;
baseUrl: string;
deprecated: boolean;
}
And the service (just the relevant code)
private _apps: Application[] = [];
constructor(private _http: HttpClient) {
this.getAllApplications().subscribe(apps => {
console.log('Apps subscriber');
this._apps = apps;
console.log('Apps subscriber Ends ' + apps);
},
err => {
console.log(err.status); // 401
console.log(err.error.error); // undefined
console.log(JSON.parse(err.error).error); // unauthorized
});
}
private getAllApplications() {
return this._http.get<Application[]>('http://development:4300/api/v1/apps');
}
From the constructor the function which gets the information from WebAPI is triggered, and the remote call is successful, but the variable this._apps is an empty array if I try to call it from anywhere in the code. I could not determine the type of the parameter "apps" in the subscribe function, but for some reason it cannot be assigned and the best answer given is that it is a function (See my first update) in one of my tries. Currently it returns in the console "[object Object]", but apps[0] gives undefined, so it is an empty Array.
This is the console output, just starting the application:
Angular is running in the development mode. Call enableProdMode() to enable the production mode.
Refreshing apps cache calling http://development:4300/api/v1/atbc-apps
Apps subscriber
Apps subscriber Ends [object Object]
I was trying this solution among many others that I forget (to use the more modern HttpClient instead the Http I used before), so what I'm doing wrong?
Update 1
I changed the constructor to this:
constructor(private _http: HttpClient) {
this.getAllApplications().subscribe(apps => {
console.log('apps length ' + apps.length);
this._apps = apps; // Remember private _apps: Application[] = [];
console.log('Apps subscriber Ends ' + apps.toString);
},
err => {
console.log(err.status); // 401
console.log(err.error.error); // undefined
console.log(JSON.parse(err.error).error); // unauthorized
});
}
and the declaration of the function called into this:
private getAllApplications(): Observable<Application[]> {
// the exactly the same as before
}
And now I got from the console this:
apps length undefined
Apps subscriber Ends
function () {
if (this instanceof Promise) {
return PROMISE_OBJECT_TO_STRING;
}
return originalObjectToString.apply(this, arguments);
}
That is the function I was talking about. Any ideas about why even though there is no errors (nor at compile time, neither at runtime), the returning object is not a real Application array?
Change this line:
private _apps: Application[] = [];
to:
_apps: Application[] = [];
Which will default to making it public. Then this line will see it:
this._apps = apps;
At the end I suppose is a mindset to work with Observables, and I tried to build a kind of cache, so the only way I could do it (let me know if there is a better way) was using the view to fill-out the cache. I could not do it from the service itself because the calling the function from the view is synchronous and to fill out the array is async. So I had to create a public setApplicationCache procedure which is filled out after calling the service from the view, it call the setApplicationCache( Application[] ) function and the rest works because it takes just the cache to do filtering and other operations or use it from other pages w/o calling the database again and again.
This is the code from the first view called (main page)
ngOnInit() {
this._myService.getAllApplications().subscribe(arrObjApps => {
this._myService.setApplicationsCache(arrObjApps)
this.listApps = this._myService.getApplications(true);
});
And the service has this functions:
private _apps: Application[] = [];
getAllApplications(): Observable<Application[]> {
return this._http.get('http://development:4300/api/v1/atbc-apps').pipe(
map( (response: Response) => {
let results = response.json().data.map( app => {
return new Application(app.name, app.baseUrl, app.deprecated);
});
return results;
})
);
}
getApplication(appName: string): Application {
return this._apps.find(app => app.name == appName);
}
getApplications(onlyActives: boolean): Application[] {
if (onlyActives) {
return this._apps.filter(app => app.deprecated == false);
} else {
return this._apps;
}
}
And as I stated the solution should be obvious. Just again the async mindset required to work with observables.

Get past request without waiting for response (Angular 2 +)

I am looking for data in an API via Get request, I need the data inside my OnInit to use in composing other data. The problem is that the method is being called but it is as an async method (without await), it passes everywhere but when the return is obtained the excution of the main method has already been finished with no results. I tried the implementation of asynchronous methods but it did not solve.
service:
getObjects(): MyClass[] {
let objects = new Array<MyClass>();
this.obterTodos<MyClass>(this.uriApi)
.map(res => res.map(x => new MyClass(x.Description, x.Id)))
.subscribe(entities => {
objects = entities;
});
return objects ;
}
get request
public getObjects<TEntity>(url: string): Observable<TEntity[]> {
return this.httpService
.get(this.serviceUrl + url, this.options)
.map(res => res.json())
.catch(this.handleError);
}
component:
ngOnInit() {
this.myObjects= this.setorService.obterSetores();
console.log('this.myObjects is empty here');
}
so you'll want to subscribe to your observable in the component. This is typically done so the component can determine when the http request should be ran & so the component can wait for the http request to finish (and follow with some logic).
// subscribe to observable somewhere in your component (like ngOnInit)
this.setorService.obterSetores().subscribe(
res => {
this.myObjects = res;
},
err => {}
)
now the component knows when the http request finishes

Angular 2 sees my Json object as an function

I have a webapp that gets via Json stuff put it in objects to display them.
I already did it two times with services and classes.
But now i copy and paste the old code make some slight changes to make sure it redirect to the good classes but now i get an array with functions instead an array with objects.
Here is my constructor that calls upon the the service classes and send things to the console
constructor(private featureService : FeatureService, private scenarioservice : ScenarioService, private failuresService : FailuresService){
//hier worden de features en failures opgehaald
this.featureService.getFeatures().subscribe(res => {
this.featureArray = res.getFeatureArray();
console.log(res.getFeatureArray());
});
this.failuresService.getFailures().subscribe(res => {
this.failureArray = res.getFailureArray();
console.log(res.failures[0].method);
console.log(this.failureArray);
});
}
}
Here is failuresService.getFailures:
import { Injectable } from "#angular/core";
import {Http, Response} from "#angular/http";
import {Failure} from "./Failure";
import {Failures} from "./failures";
#Injectable()
export class FailuresService {
constructor(protected http: Http) {}
getFailures() {
return this.http.get('http://localhost:8080/testresultaten')
.map((response: Response) => response.json())
.map(({failures = [Failure]}) => new Failures(failures));// deze error is bullshit
}
}
This is the Json that I get and want to get in an class:
{
"failures:": [
{
"method": "canRollOneGame",
"object": "BowlingGame.GameTest"
},
{
"method": "canCountNighteeneight",
"object": "FizzBuzz.FizzBuzzTest"
}
]
}
Here are the Failure and Failures classes:
import {Failure} from "./Failure";
export class Failures {
constructor(public failures : Failure[]){}
public getFailureArray(): Failure[]{
return this.failures;
}
}
export class Failure{
constructor(public method : String , public object : String ){ }
}
I could be wrong but this doesn't look right:
getFailures() {
return this.http.get('http://localhost:8080/testresultaten')
.map((response: Response) => response.json())
.map(({failures = [Failure]}) => new Failures(failures));// deze error is bullshit
}
That last mapping, I'm not sure what it is supposed to do. I've never seen that syntax (doesn't mean it's wrong). I get that you have an incoming array. But what this syntax does {failures = [Failure]} is what I think your problem is. I don't think that will do what you think.
Try this and see what happens:
.map(failures => { console.log ( failures ); new Failures(failures) } );// deze error is bullshit
If you try that with your old mapping, and then this new one, it'd be interesting to see what that console.log traces out. I think doing it your way, you won't see what you expect.
If that sorts you out, then you can try typing the incoming payload (though I'm not sure it'll work; it should be receiving JSON from the previous mapping).
.map( (failures: Array<Failure>) => { console.log ( failures ); new Failures(failures) } );
So if I read it correctly: Your goal is to create an array of Failure objects with the values coming from a JSON array where each of this has a method you'd like to execute somewhere in the code.
If thats the case then I understand what went wrong. As Tim Consolazion mentioned in the comments you only get the values for the Failure object not the Failure object itself (See custom created method error: "is not a function"). So in order to execute the commands from Failure you have to create for each object in your JSON array a Failure object. Something like this:
return this.http.get('http://localhost:8080/testresultaten')
.map((response: Response) => {
let content = response.json();
let failureList: Failure[] = [];
content.forEach(failure => {
failureList.push(new Failure(failure.method, failure.object))
});
return failureList || {};
}
What I wonder is where you found {failures = [Failure]}? I've never seen this before and I've no idea what this is for.
EDIT: I edited the question so it fits your class Failure. The failure in content.forEach(failure => { in an object from your JSON array. You can access the values of it like it is a normal object. In your case with failure.method and failure.object.
I found my problem in the Json the name was function: but in my code it was function so all that was necessary was to make sure it sends function instead of function:

How to modify JSON Object retrived from MongoDB?

So I am making a rest call to get this JSON Object:
{
"_id":"57a0811276e75ba815d248b0",
"gName":"demo-layout",
"gType":"Content",
"wsId":"57a036c376e75ba815d248ac",
"desc":"Demo-Layout for rapidpage",
"createdDate":"2016-08-02T11:16:34.223Z",
"__v":0
}
Now I want to add an array to this object ,something like this:
{
"_id":"57a0811276e75ba815d248b0",
"gName":"demo-layout",
"gType":"Content",
"wsId":"57a036c376e75ba815d248ac",
"desc":"Demo-Layout for rapidpage",
"createdDate":"2016-08-02T11:16:34.223Z",
"blocks":[], //should be added at runtime
"__v":0
}
So I tried following:
dbPage:any={};
ngOnInit(){
let pageId:string="57a0811276e75ba815d248b0";
this._pagesService.getPageById(pageId).subscribe((Page)=>{
this.dbPage=rapidPage;
console.log(this.dbPage); //this prints the object as shown above
});
this.dbPage.blocks=[];
this.dbPage.blocks.push(block1);
}
But its not modifying the current object,instead its creating new Object as :
{blocks: Array[]}
any inputs?
That's because you're not assigning it in the subscribe call. Due to the async nature of HTTP requests in JavaScript, the code below the subscribe call will be executed before the callback inside the subscribe call.
You can easily fix this by moving the code inside the callback:
dbPage: any = {};
ngOnInit(){
let pageId: string = "57a0811276e75ba815d248b0";
this._pagesService.getPageById(pageId).subscribe((rapidPage) => {
this.dbPage = rapidPage;
console.log(this.dbPage); //this prints the object as shown above
this.dbPage.blocks = [];
this.dbPage.blocks.push(block1);
});
}

LocomotiveJS access response JSON in controller's after filter

I'm looking for a way to access the JSON being sent back to the requestor in the "after" filter for a controller.
var locomotive = require('locomotive');
var myController = new locomotive.Controller();
myController.after('myAction', function(next) {
var response = {}; //I want to access the JSON being sent back in myAction: {'hello':'world'}
console.log(response); //this should log "{'hello':'world'}"
next();
});
myController.myAction = function myAction() {
this.res.json({'hello':'world'});
}
module.exports = myController;
If anyone has any way of doing this, it would be much appreciated.
In your main action, assign your json to an object on this (res is reserved):
myController.myAction = function myAction() {
this.model = {'hello':'world'};
this.res.json(this.model);
}
Then you can access it in your after filter:
myController.after('myAction', function(next) {
var model = this.model;
console.log(model);
next();
});
I found a "hack" solution... It's not the cleanest, and requires changing the code within the express response.js file in "node_modules"...
If anyone has a better option where you can access the json being sent in response to the request within the controller action (or controller filter) itself, I'd greatly appreciate it.
Thanks.
in the ~/node_modules/locomotive/node_modules/express/lib/response.js file, I altered the "res.json" function (line 174 for me) to include the following line after the declaration of the body variable (which is passed to the send function).
this.responseJSON = body;
This allows you to access this.responseJSON within a controller's after filter, as follows:
myController.after('myAction', function(next) {
**var response = this.res.responseJSON; //ACCESS RESPONSE JSON HERE!!!!!**
console.log(response); //Now logs "{'hello':'world'}"
next();
});
Like I said, not the most elegant, but gets the job done in a pinch. Any more elegant solutions welcome...