Yes another Aurelia question, apologies!
So I'm trying to access data within my view passed from a model, whilst I can see the data within the response, I cannot seem to get it to display on the view. Any help greatly appreciated.
I've tried a few things but I guess being new to Aurelia,ES6 and promises, it's throwing me out a little or I've been staring at to long.
//EDIT Data Access Component
import {inject} from "aurelia-framework";
import {HttpClient} from "aurelia-http-client";
let baseUrl = "/FormDesigner";
#inject(HttpClient)
export class FormData{
constructor(httpClient)
{
this.http = httpClient;
}
GetFormById(formId)
{
return this.http.get(`${baseUrl}/GetFormById/${formId}`)
.then(f => f.content);
};
}
Model:
activate(params)
{
return this.form.GetFormById(params.formId)
.then(f => this.form = f);
}
The View:
<p class="navbar-text navbar-left">
${form.name}
</p>
The Response:
{"Id":"x","OrganisationId":"x","OrganisationDepartmentId":null,"ScheduleId":null,"DefinitionTypeId":"x","ReferenceNumber":11171,"Name":"New Form Test External Access","Description":"","IsTemplate":true,"IsActive":true,"IsSingleFormTemplate":false,"MinimumInstances":null,"MaximumInstances":null,"IsAdhocCreationEnabled":false,"HasCalculation":false,"Calculation":null,"Recalculate":true,"IsHidden":false}
So again I don't see the data appearing on the view and I feel I'm missing something rather simple.
//EDITS
So after a little digging I made a little change to my API returning a JSON array rather than a JSON object and also switched Aurelia to use Fetch... So now I can access the data in my data component but not my model - rather frustrating!
import {inject} from "aurelia-framework";
//import {HttpClient} from "aurelia-http-client";
//import 'fetch';
import {HttpClient} from 'aurelia-fetch-client';
let baseUrl = "/FormDesigner";
#inject(HttpClient)
export class FormData{
constructor(httpClient)
{
httpClient.configure(config => {
config
.withBaseUrl('/FormDesigner')
.withDefaults({
credentials: 'same-origin',
headers: {
'Accept': 'application/json',
'X-Requested-With': 'Fetch'
}
})
.withInterceptor({
request(request) {
console.log(`Requesting ${request.method} ${request.url}`);
return request;
},
response(response) {
console.log(`Received ${response.status} ${response.url}`);
return response;
}
});
});
this.http = httpClient;
}
GetFormById(formId)
{
return this.http.fetch(`/GetFormById/${formId}`)
.then(response => response.json())
.then(data => {
//Log here, to check incoming data
console.log("From component: " + data.Name);
//This WORKS
});
};
}
Again I've created an abstraction where as my model does not need to know about calls to the server.
import {inject} from "aurelia-framework";
import {FormData} from "form/formData";
#inject(FormData)
export class Form
{
constructor(formData)
{
this.form = formData;
}
activate(params)
{
if(params.formId != null)
{
return this.form.GetFormById(params.formId)
.then(data =>
{
this.form = data
console.log(this.form.Name);
//This does not work!!
});
}
else
{
//Show message that param does not exist or redirect to no form page
console.log("No Id");
}
}
}
Any help greatly appreciated,
Most likely you need to deserialize the JSON response into a Javascript object using JSON.parse.
GetFormById(formId)
{
return this.http.get(`${baseUrl}/GetFormById/${formId}`)
.then(response => JSON.parse(response.content));
};
Related
I'm really new with Next.js and Nest.js and I can't figure out what's going wrong here.
I have a backend nest.js app serving a json api on http://localhost:3081/v1/transactions.
If I try to do a GET request from postman all works fine.
This is my index.tsx in the next.js frontend app:
import type { GetStaticProps, NextPage } from "next";
import Head from "next/head";
import Image from "next/image";
import styles from "../styles/Home.module.css";
import { GetTransactionsResults, Transaction } from "../transactions.types";
const Home: NextPage<{ transactions: Transaction[] }> = ( { transactions }) => {
return (
<div className={styles.container}>
<main className={styles.main}>
<Image src={"/logo.png"} width={120} height={32} />
{transactions.map((transaction) => {
return <li key={ transaction.id }>{ transaction.asset }</li>
})}
</main>
</div>
);
};
export const getStaticProps: GetStaticProps = async (context) => {
const res = await fetch("http://localhost:3081/v1/transactions");
const { results }: GetTransactionsResults = await res.json();
return {
props: {
transactions: results,
},
};
};
export default Home;
and this is the Interface in transaction.type.ts:
export interface GetTransactionsResults {
info: Info;
results: Transaction[];
}
export interface Info {
count: number;
page: number;
next: string;
prev: null;
}
export enum TransactionNature {
Deposit = "Deposit",
Withdraw = "Withdraw",
Rewards = "Rewards",
Interest = "Interest",
}
export interface Transaction {
id: string
nature: {
code: TransactionNature
}
amount: number
asset: string
user: {
id: string
}
}
So if I try to load the frontend I get this error message:
Server Error
Error: Error serializing `.transactions` returned from `getStaticProps` in "/".
Reason: `undefined` cannot be serialized as JSON. Please use `null` or omit this value.
It seems like an empty response from the backend app...
I also tried to fetch data from another web api like this one: https://rickandmortyapi.com/api/character/ and it works.
Sure I miss something here, sorry if it is a dumb question but I'm really new.
Ok I figured out how to solve it.
I followed the documentation and rewrite the function in this way:
import type { NextPage } from "next";
import Head from "next/head";
import Image from "next/image";
import styles from "../styles/Home.module.css";
import { GetTransactionsResults, Transaction } from "../transactions.types";
const Home: NextPage<{transactions: Transaction}> = ( { transactions } ) => {
return (
<div className={styles.container}>
Object.values(transactions).map(transaction => {
return <li key={transaction.id}>{transaction.asset}</li>
})
}
</div>
);
};
//Get API data
export async function getStaticProps() {
// Call an external API endpoint to get posts.
// You can use any data fetching library
const res = await fetch('http://localhost:3081/v1/transactions');
const results: GetTransactionsResults = await res.json()
// By returning { props: { transactions } }, the Home component
// will receive `transactions` as a prop at build time
return {
props: {
transactions: results,
},
}
}
export default Home;
I made an api call and get a response as below, shown on console
My Api Provider:
import { HttpClient, HttpParams, HttpHeaders } from '#angular/common/http';
import { Injectable } from '#angular/core';
import 'rxjs/add/operator/map';
#Injectable()
export class RestapiProvider {
apiUrl = 'http://example.com/api/GetItemByCategory?cat_id=1&merchant_id=1&json=true';
constructor(public http: HttpClient) {
//console.log('Hello RestapiProvider Provider');
}
getItembyCategory() {
return new Promise(resolve => {
this.http.get(this.apiUrl).subscribe(data => {
resolve(data);
console.log(data);
}, err => {
console.log(err);
});
});
}
Console.log(data) shows
[object Object]: {code: 1, details: Object, msg: "success..", request: "{"cat_id":"1","merchant_id":"1","json":"true"}"}
console.log(data)
I need to parse the json data on 'details: Object'
I tried console.log(data.details)
got error:
Unable to get property 'details' of undefined or null reference
UPDATE
I was trying to use map operator as below
this.http.get(this.apiUrl).map(data => data.json()).subscribe(data => {
resolve(data);
console.log(data);
}
Got another error: property json does not exist on type 'Object'
Please let me know how to make it work on ionic 3.
Regards
console.log SS
I manage to go through the 'details' with
data['details'];
and 'item'
data['details']['item'];
Thanks for all the suggestions.
Why don't you try like this:
Import the toPromise operator:
import 'rxjs/add/operator/toPromise';
Then turn your observable into a promise with it's help. No need to map the response to Json as you use the new Angular HttpClient, it is mapped by default.
getItembyCategory() {
return this.http.get(this.apiUrl)
.toPromise()
.then((data: any) => {
console.log('Success', data.details);
return data;
})
.catch(err => {
console.log('Error', err);
return err;
})
}
This way the code is so much cleaner. It is tested and works. If it doesn't then you should check your apiUrl.
Edited
The reason you receive the error property 'details' does not exist on type 'Object' is because you need to give a type to the data object you receive (other than the default Object). I updated the example with this:
(data: any) => {
console.log(data.details);
return data;
}
This is so much better than the ugly square brackets in data['details'] :).
i am new to angular 2 and to observables but i wanted to give it a shot. So i have installed the angular-cli and made a simple test project.
All i wanted it to do is read a json file and work with the data inside of a component (the first intention was to make a service but i wanted to start on a low basis).
So i have created a json file in the assets/json folder (testjson.json):
{
"teststring": "test works"
}
then i have imported the http from angular and the rxjs map stuff inside of my content.component.ts file:
import { Component, OnInit } from '#angular/core';
import { Http } from '#angular/http';
import 'rxjs/add/operator/map';
#Component({
selector: 'app-content',
templateUrl: './content.component.html',
styleUrls: ['./content.component.css']
})
export class ContentComponent implements OnInit {
title: string = "Default";
data;
constructor(private http:Http) {
http.get('assets/json/testjson.json').map(res => res.json()).subscribe(data => {this.data = data; this.title = data.teststring; console.log(this.data);});
}
ngOnInit() {
}
}
So far so good, the app prints out the following:
app works!
test works [object Object]
But i want to use this data in the whole component, not only in the constructor. but if i try to console.log "this.data" outside of the constructor (inside the ngOnInit function), it prints undefined in the console.
I know, that it must have something to do with asynch loading but unfortunately i have no clue how to tell the app to wait until this.data is filled.
I hope you can help me with that. Of course in the future i want a service which does that kind of stuff and more than one component should grab data from it.
Thanks in advance!
You should move the initialization code to the initialization method.
Your data becomes available once the callback completes. In your template you can use *ngIf to execute code inside a block once there is data. As long as the *ngIf does not eval to true the inner code will not run.
The only way you can run console.log(data) is from inside the callback or called from the callback because you have to wait until the data is loaded.
content.component.html
<div *ngIf="data">
<span>{{data.teststring}}</span>
</div>
content.component.ts
export class ContentComponent implements OnInit {
title: string = "Default";
data: any = null;
constructor(private http:Http) {
}
ngOnInit() {
this.http.get('assets/json/testjson.json')
.map(res => res.json())
.subscribe(data => {
this.data = data;
this.title = data.teststring;
console.log(this.data);
});
}
}
Edit
In response to the comment below If you abstract out the http call to a service you can see the exact same logic still applies. You are still using the concept of a promise of data and that you can subscribe to that promise once it has completed. The only difference here is the http call is abstracted to a different class.
content.component.ts
export class ContentComponent implements OnInit {
title: string = "Default";
data: any = null;
// inject service
constructor(private contentService:ContentService) {
}
ngOnInit() {
this.contentService.getData()
.subscribe(data => {
this.data = data;
this.title = data.teststring;
console.log(this.data);
});
}
Service
export class ContentService {
constructor(private http:Http) {
}
getData(): IObservable<{teststring:string}> { // where string can be some defined type
return http.get('assets/json/testjson.json')
.map(res => res.json() as {teststring:string});
}
I have trying to get json data from when i calling service method. I got a return object but i didn't get json data. Which part i have to change?
"goods.services.ts"
getAllData(): Observable<Product>{
let jwtTok1 ="Something";
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Authorization', 'Token ' + jwtTok1);
let options = new RequestOptions({ headers: headers, body: {} });
return this.http.get(BASE_URL_GOODS, options)
.map((res:Response) => {return Observable.of ({type: "success", payload: res.json().data})})
.catch(error => this.handleError(error));
}
Another is test case file "goods.component.spec.ts"
import {
inject,
TestBed,fakeAsync,tick
} from '#angular/core/testing';
import { Component } from '#angular/core';
import {
BaseRequestOptions,
ConnectionBackend,
Http
} from '#angular/http';
import { MockBackend,MockConnection } from '#angular/http/testing';
// Load the implementations that should be tested
import { Observable } from 'rxjs/Rx';
import { AppState } from '../app.service';
import { ActionReducer, Action, Store } from '#ngrx/store';
import { AppStore } from '../models/appstore.model';
import {goodsDescriptionComponent } from './goods-desc.component';
import { goodsService } from '../common/service/goods.service';
import { goodsReducer } from '../common/reducers/goods.reducer';
import {provideStore} from '#ngrx/store';
import { goods} from '../common/models/goods.model';
import { Input } from '#angular/core';
describe('GoodsDescriptionComponent', () => {
//let store:Store<AppStore>;
beforeEach(() => TestBed.configureTestingModule({
providers: [
BaseRequestOptions,
MockBackend,
{
provide: Http,
useFactory: function(backend: ConnectionBackend, defaultOptions: BaseRequestOptions) {
return new Http(backend, defaultOptions);
},
deps: [MockBackend, BaseRequestOptions]
},
goodsService, provideStore({goodsData: goodsReducer}),goodsDescriptionComponent
]}));
it('should url will be same ',
inject(
[goodsService, MockBackend],
fakeAsync((service:ProductsService, backend: MockBackend) => {
backend.connections.subscribe((connection: MockConnection) => {
expect(connection.request.url).toBe(
'http://localhost:3001/goodsMS/');
});
service.getAllData();
console.log("goods what we got: ",service.getAllData());
})));
});
Getting Response Result is,
This Response object getting from console in google chrome. Still i can't get correct solution for getting json data from service method call.i can't reach json server. my json server URL is , "http://localhost:3001/goods". please anyone help me. Thanks in advance.
You need to set a response on the connection
backend.connections.subscribe((connection: MockConnection) => {
expect(connection.request.url).toBe(
'http://localhost:3001/goodsMS/');
connection.mockRepond(new Response(new ResponseOptions({
body: `{"some":"json", "response":"body"}`
}));
});
You need to subscribe to your service call
service.getAllData().subscribe((result) => {
expect(somethingWithData)
})
tick()
since you are using fakeAsync, you need to tick to force completion of the asynchronous observable. If you use async instead of fakeAsync, then you don't need to call tick
You should not be returning an Observable in the service method map function. Just return a normal object.
.map((res:Response) => {type: "success", payload: res.json().data})
I'm learning Ang2. I ran the Hero tutorial successfully. to practice, I just added a link in the main page to a get a new component. there is a json file with a list of Radio Station. the following are my service, and the Component:
import { Radio } from '../models/radio';
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import 'rxjs/Rx';
#Injectable()
export /**
* RadioService
*/
class RadioService {
radios: Radio[] = [];
len: Number = 0;
http: Http;
constructor(http: Http) {
this.http = http;
}
getRadios() {
console.log('Radios Service is being called');
this.http.get('app/mock/FranceRadioJSON.json')
.map((res: Response) => res.json())
.subscribe((rad: Radio[]) => { // Succes
console.log('Radios Service Success');
this.radios = rad;
this.len = rad.length;
}
, err => {// Failure
console.log('Radio Services Failure');
console.error(err);
}
, // complete
() => {
console.log('Get Radios Complete');
console.log('Records NB: ' + this.len);
console.log('radios nb: ' + this.radios.length)
}
)
;
return this.radios;
}
and the Component is:
import { Component, OnInit } from '#angular/core';
import { RouteParams } from '#angular/router-deprecated';
import { Radio } from '../../models/radio';
import { RadioService } from '../../services/radio.service';
#Component({
selector: 'radio'
, templateUrl: 'app/html/radio.component.html'
, providers: [RadioService]
})
export /**
* RadioComponent
*/
class RadioComponent {
radios: Radio[] = [];
constructor(
private radioservice: RadioService) {
}
getRadios() {
this.radios = this.radioservice.getRadios();
console.log('radio component NB: ' + this.radios.length);
}
ngOnInit() {
this.getRadios()
}
}
the problem is the call of service is coming first, and no Radio is return while when the service is called with console.log I see that's is successful and get all records from JSON file. Any help would be greatly appreciated.
You can't get data this way from async calls (which is when you use Promise or Observable). Http returns and Observable. You need to subscribe() and in the callback you pass to subscribe(...) you get the data and can assign it to local properties.
And example how you can solve this:
In getRadios we don't call subscribe() and use map() instead. map() returns an Observable which allows the caller to get the data. subscribe() returns a Subscription and doesn't allow to get the response data, it only allows to cancel the subscription (which is also often handy, but not now ;-) ).
getRadios() {
console.log('Radios Service is being called');
return this.http.get('app/mock/FranceRadioJSON.json')
.map((res: Response) => res.json())
.map((rad: Radio[]) => { // Succes
console.log('Radios Service Success');
this.radios = rad;
this.len = rad.length;
});
}
We subscribe in getRadios because here we want to get the data. When we subscribe, the map() calls in getRadios are executed as well. subscribe() makes the Observable actually do its work:
getRadios() {
this.radios = this.radioservice.getRadios()
.subscribe((radios) => {
this.radios = radios;
} , err => {// Failure
console.log('Radio Services Failure');
console.error(err);
} , // complete
() => {
console.log('Get Radios Complete');
console.log('Records NB: ' + this.len);
console.log('radios nb: ' + this.radios.length)
});
// executed before the HTTP get call to the server
// console.log('radio component NB: ' + this.radios.length);
}
Because data is not returned immediately but only when the callback passed to subscribe(...) is executed you can get errors for bindings like
<div>{{radios.xxx}}</div>
when radios is still null when Angular2 already resolves the bindings in the template. To avoid errors you can use the safe-navigation (Elvis) operator
<div>{{radios?.xxx}}</div>
Angular2 doesn't try to access .xxx while radios is null.
The problem is that you are subscribing to your observable from within your service. Your service should return an Observable and then you Subscribe to it from your component.
So your service should be:
getRadios() : Observable<Radio[]> {
return this.http.get('app/mock/FranceRadioJSON.json')
.map((res: Response) => <Radio[]>res.json())
}
And your component:
getRadios() {
this.radios = this.radioservice.getRadios().subscribe(
r => {
this.radios = r;
}
...
);
}