Passing JSON Object with NavController - json

I want to pass a JSON Object to another Page in Ionic 2.
I tried like the following, but I get the exception
Cannot read property 'user' of undefined.
Here
How to pass data in ionic2 someone asked the same, but it's without a clear, working answer.
var jsonData = JSON.parse(data);
this.navCtrl.push(InfoPage, {jsonData});
InfoPage
export class InfoPage {
jsonData = null;
user = null;
constructor(public navCtrl: NavController, private loadingCtrl: LoadingController, private navParams: NavParams) {
this.jsonData = this.navParams.get("jsonData");
}
ngOnInit()
{
this.user = this.jsonData.user;
console.log(this.user);
}
}

Not entirely sure, but i would think something like this would work. On the other hand, I would think that using an intermediary service for this will work much better.
On the page you want to send data from:
let jsonData = JSON.stringify(data);
this.navCtrl.push(InfoPage, {jsonData : jsonData});
On the page which retrieves the data:
export class InfoPage {
jsonData = null;
user = null;
constructor(private navParams: NavParams) {
this.jsonData = JSON.parse(this.navParams.get("jsonData"));
}
ngOnInit() {
this.user = this.jsonData.user;
console.log(this.user);
}
}
Let me again mention though, that it's better to store such data in a so called PageService which will be a singleton throughout your pages. You can use this to send data from one page to another. If you really want to stick with the parse/stringify, then I would expect my solution to work. Be aware that I didn't test it.
A second solution would be the use of a service, which perhaps you can use for other use-cases as well:
export class PageService {
public pageData: any;
}
You should add this service to your root providers array. After that you can use it inside your pages like this:
FirstPage (sender)
export class FirstPage {
private jsonData: any = {user: 'saskia'};
constructor(private pageService: PageService) {}
goToInfoPage(): void {
this.pageService.pageData = this.jsonData;
this.navCtrl.push(InfoPage);
}
}
InfoPage (receiver)
export class InfoPage {
user: string;
constructor(private pageService: PageService) {}
ionViewWillEnter() {
this.user = this.pageService.pageData.user;
console.log(this.user); //saskia
}
}
I'm using the ionViewWillEnter lifecycle hook, to assure that the data is obtained at the right time

Related

How to get a specific value from a JSON object, like a name?

I use Angular as the front end of my application. For the backend I use glassfish. I currently use a Http GET verb to get a JSON object with an id and name and address. I only want to get the name of the object, how do I do that in a typescript file? How do I get the name of the newest added object of the rest server?
I want to get restaurantName from the object:
{ restaurantId: 1, restaurantName: 'Mcdonalds', restaurantAdres: 'Kalverstraat 5' },
Code that retrieves the object from the rest server:
ngOnInit() {
this.http.get('http://localhost:8080/aquadine-jee/resources/restaurant')
.subscribe(
val => {
const restStr = JSON.stringify(val);
console.log(restStr);
);
Backend code:
#GET
#Produces("application/json")
public Response all(){
List<Restaurant> all = repositoryService.getAllRestaurants();
return Response
.status(200)
.header("Access-Control-Allow-Origin", "*")
.entity(all)
.build();
}
public List<Restaurant> getAllRestaurants() {
EntityManager em = entityManagerFactory.createEntityManager();
List<Restaurant> restaurants = em.createQuery("SELECT r FROM Restaurant r").getResultList();
em.close();
return restaurants;
}
#Entity
#Table(name = "restaurant")
#NamedQueries({
#NamedQuery(name = "Restaurant.findOne", query = "select m from Restaurant m where m.id = :id"),
#NamedQuery(name = "Restaurant.getAll", query = "select m from Restaurant m")
})
public class Restaurant implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
// #Column(name="naam")
// #NotBlank
// private String naam;
// #NotBlank
String restaurantName;
// #NotBlank
String restaurantAdres;
int restaurantId;
public Restaurant(){
}
public Restaurant(int restaurantId, String restaurantName, String restaurantAdres) {
this.restaurantId = restaurantId;
this.restaurantName = restaurantName;
this.restaurantAdres = restaurantAdres;
}
First, I'd create a class and maybe an interface to give you strongly typed objects in your TypeScript:
Then you can return the object from your get request as that object and use that however you want like result.restaurantName
A quick mock of what that looks like (using a stub method instead of http) is here:
In short though:
the class and interface for Angular:
export interface IRestaurant {
restaurantId: number;
restaurantName: string;
restaurantAddres: string;
}
export class Restaurant implements IRestaurant {
restaurantId:number;
restaurantName:string;
restaurantAddres:string;
}
And then the component that gets the data via a method, and uses essentially the JSON as an object:
export class AppComponent {
favoriteRestaurant: IRestaurant;
retrieve(){
this.favoriteRestaurant = this.getRestaurant();
alert(this.favoriteRestaurant.restaurantName);
}
getRestaurant():IRestaurant {
var result: Restaurant = {
restaurantId: 1,
restaurantName: 'Mcdonalds',
restaurantAddres: 'Kalverstraat 5'
};
return result;
}
}
Making it more useful for you though, change the http.get method to something like this:
ngOnInit() {
this.http.get('http://localhost:8080/aquadine-jee/resources/restaurant')
.subscribe(
val:IRestaurant => {
this.favoriteRestaurant = val;
console.log(restStr.restaurantName);
);
You don't want to use JSON.stringify above, because that gives you a string!
Additionally, your restaurantAddress is mispelled, these would need to match exactly. So I would correct the backend.

How Ionic constructor works

I have a problem with ionic constructor. Actually a Java program constructor works line by line. But in my Ionic application I have called a back-end service and getting some data and populate my variables with the received data.
constructor(public navCtrl: NavController, public navParams: NavParams, public patientService: PatientServiceProvider, public scanner: BarcodeScanner) {
this.id = this.navParams.get('id');
this.patientService.searchPatientById(this.id).subscribe(res => {
this.id = res.id;
this.mobile = res.mobile;
this.fname = res.firstname;
this.lname = res.lastName;
console.log("print this:"+this.mobile);
});
this.mobile = this.navParams.get('mobile');
console.log(this.id);
console.log("****"+this.mobile);
}
In above code my service working fine. My problem is last line execute before the service call. That means my output will look like
5656
"****"+94758968989
print this:undefined
Why this happened. This is not executed line by line.
Your patientService returns an Observable, so so functionCall searchPatientById() is asyncron. That means that after the Functioncall the next lines will be executed and the part in the subscribe is executed when you get a result.
To avoid that, you have more options, for example you can create another method which should be executed AFTER the async call and than call that method at the end in the subscribe. Or you can use async/await like this:
constructor(public navCtrl: NavController, public navParams: NavParams, public
patientService: PatientServiceProvider, public scanner: BarcodeScanner) {
this.init();
}
async init() {
this.id = this.navParams.get('id');
let res = await this.patientService.searchPatientById(this.id);
this.id = res.id;
this.mobile = res.mobile;
this.fname = res.firstname;
this.lname = res.lastName;
console.log("print this:"+this.mobile);
this.mobile = this.navParams.get('mobile');
console.log(this.id);
console.log("****"+this.mobile);
}

HttpClient how to convert nested json and map to model domain

Hi I am pretty new to programing and i have been looking for an answer on the web for days for my problem without finding anything that works for me no matter what i do. I am receiving nest json as response. The json object amongst other things seem to have a dynamic key. What i want is to transform the json object to my model so that i can easily access the data in my template. Appreciate any help
here is an example of the json data
Amadeus API response
Here is my service
getResults(params: any) {
this.getItiniraries(params).subscribe((res) => {
this.itinirary = res;
// console.log(res);
this.updatedResults.next(this.itinirary);
});
}
getItiniraries(params: any): Observable<Itinirary> {
return this.http.get<Itinerary>('http://localhost:4202/api/itinirary' , {params: params})
; }
the models
Itinirary model
import { Result } from './result.model';
import { Meta } from '#angular/platform-browser';
// Model for data from Amadeus flight affiliate search
export class Itinirary {
public meta: Meta;
public results: Result[];
constructor(res: any) {
this.meta = res.meta;
this.results = res.results;
}
}
Result Model
import { Flight } from './flight.model';
import { Fare } from './fare.model';
export class Result {
public outbound_duration: string;
public outbound_flights: Flight[];
public inbound_duration: string;
public inbound_flights: Flight[];
public fare: Fare;
public cabin_code: string;
public fare_family: string;
public travel_class: string;
public merchant: string;
public airline: string;
public deep_link: string;
constructor(result: any) {
this.outbound_duration = result.outbound.duration;
this.outbound_flights = result.outbound.flights;
this.inbound_duration = result.inbound.duration;
this.inbound_flights = result.inbound.duration;
this.fare = result.fare;
this.cabin_code = result.cabin_code;
this.fare_family = result.fare_family;
this.travel_class = result.travel_class;
this.merchant = result.merchant;
this.airline = result.airline;
this.deep_link = result.deep_link;
}
}
Flight model
import { BookingInfo } from './bookingInfo.model';
export class Flight {
public departs_at: Date;
public arrives_at: Date;
public marketing_airline: string;
public operating_airline: string;
public flight_number: number;
public aircraft: number;
public booking_info: BookingInfo;
public origin_airport: string;
public origin_terminal: string;
public destination_airport: string;
public destination_terminal: string;
constructor(flight: any) {
this.departs_at = flight.departs_at;
this.arrives_at = flight.arrives_at;
this.marketing_airline = flight.marketing_airline;
this.operating_airline = flight.operating_airline;
this.flight_number = flight.flight_number;
this.aircraft = flight.aircraft;
this.booking_info = flight.booking_info;
this.origin_airport = flight.origin_airport;
this.origin_terminal = flight.origin_terminal;
this.destination_airport = flight.destination_airport;
this.destination_terminal = flight.destination_terminal;
}
}
Meta model
import { Carrier } from './carrier.model';
export class Meta {
public carriers: {[key: string]: Carrier };
constructor(meta: any) {
this.carriers = meta.carriers;
}
}
Carrier Model
export class Carrier {
public identifier: string;
public name: string;
public logoSmall: string;
public logoMedium: string;
constructor(carrier: any) {
this.identifier = carrier;
this.name = carrier.name;
this.logoSmall = carrier.logos.samll;
this.logoMedium = carrier.logos.medium;
}
}
in my flight model i would also like to add two properties date and time that derives from departure_at and arrival_at.
Basically i want to be able to pass the whole Intinirary object to my view so that i can through string iterpolation get the values.
ex after using ngFor or let result of itinirary.results
{{ result.outbound_flights[0].departure_date }} etc.
would really appreciate good guidance
updated service
getItiniraries(params: any): Observable<any> {
return this.http.get<any>('http://localhost:4202/api/itinirary' ,
{params: params})
.pipe(map((AmdResponse) => {
const parsedRes = JSON.parse(AmdResponse);
const itin = new Itinirary(parsedRes);
return itin;
} )); }
Updated meta Model and carrier Model
Meta
import { Carrier } from './carrier.model';
export class Meta {
public carriers: {[key: string]: Carrier };
constructor(meta) {
this.carriers = {};
Object.keys(meta.carriers).forEach(code => {
this.carriers[code] = new Carrier(meta.carriers[code]);
});
}
}
carrier model
export class Carrier {
public name: string;
public logoSmall: string;
public logoMedium: string;
constructor(cObject ) {
Object.keys(cObject).forEach(code => {
this.name = cObject.name;
});
Object.keys(cObject.logo).forEach(code => {
this.logoSmall = cObject.logos.samll;
this.logoMedium = cObject.logos.medium;
});
}
}
I also updated my Result model like this. Does it make sense?
import { Flight } from './flight.model';
import { Fare } from './fare.model';
export class Result {
public outbound_duration: string;
public outbound_flights: Flight[];
public inbound_duration: string;
public inbound_flights: Flight[];
public fare: Fare;
public cabin_code: string;
public fare_family: string;
public travel_class: string;
public merchant: string;
public airline: string;
public deep_link: string;
constructor(result) {
this.outbound_duration = result.outbound.duration;
// this.outbound_flights = this.loop(this.outbound_flights,
result.outbound.flights);
this.inbound_duration = result.inbound.duration;
// this.inbound_flights = this.loop(this.inbound_flights,
result.inbound.flights);
this.fare = new Fare(result.fare);
this.cabin_code = result.cabin_code;
this.fare_family = result.fare_family;
this.travel_class = result.travel_class;
this.merchant = result.merchant;
this.airline = result.airline;
this.deep_link = result.deep_link;
for (let i = 0; i < result.outbound.flights.length; i++) {
this.outbound_flights[i] = new Flight(result.outbound.flights[i]);
}
for (let i = 0; i < result.inbound.flights.length; i++) {
this.inbound_flights[i] = new Flight(result.inbound.flights[i]);
}
}
// loop(a, b) {
// for (let i = 0; i < b.length; i++) {
// a[i] = new Flight(b[i]);
// }
// return a;
// }
}
I tested both with a function or seperate loop.
I also added a dateFormatterService in my flight model, was not sure though where to import it to as i could not import it to constructor.
import { BookingInfo } from './bookingInfo.model';
import { DateFormatterService } from '../../Shared/dateFormatter.service';
export class Flight {
private df: DateFormatterService; // can i have it here instead of constructor?
public departs_date: string;
public departs_time: string;
public arrives_date: string;
public arrives_time: string;
public marketing_airline: string;
public operating_airline: string;
public flight_number: number;
public aircraft: number;
public booking_info: BookingInfo;
public origin_airport: string;
public origin_terminal: string;
public destination_airport: string;
public destination_terminal: string;
constructor(flight: any ) {
const depart_at = new Date(flight.departs_at);
const arrive_at = new Date(flight.arrives_at);
this.departs_date = this.df.transformDate(depart_at);
this.departs_time = this.df.transformTime(depart_at);
this.arrives_date = this.df.transformDate(arrive_at);
this.arrives_time = this.df.transformTime(arrive_at);
this.marketing_airline = flight.marketing_airline;
this.operating_airline = flight.operating_airline;
this.flight_number = flight.flight_number;
this.aircraft = flight.aircraft;
this.booking_info = new BookingInfo(flight.booking_info);
this.origin_airport = flight.origin_airport;
this.origin_terminal = flight.origin_terminal;
this.destination_airport = flight.destination_airport;
this.destination_terminal = flight.destination_terminal;
}}
As a preamble, understand there is no way to receive a JSON string (from an API or elsewhere) and make it become an instance of a specific custom Class.
I think you started with the right idea in your Meta constructor.... that you need to parse the received data into your own objects. You just need to go further, by explicitly parsing all properties of the received JSON into new objects (of your classes) one at a time until you're done. Tedious, maybe, but required if you want your own class hierarchy to represent the received data (which may NOT be needed, but that's not for me to decide).
I assume you've already got a JS literal object from the data received from the API response, something like this:
const itins = JSON.parse( amadeusResponse );
Then, you let your classes do the work:
const meta = new Meta( itins.meta );
//or
const alternate = new Meta();
alternate.fromJSON( itins.meta );
By the above, you can see you can either have a method that reads JSON data, or a constructor. The choice would be made by whether you would expect to ever create a Meta object without first having the JSON (in which case the method alternate is likely better).
In either case, the implementation reads the JS object you give it, and parses the received data structure into the structure you want to have in your local class instances. For example, in the Meta constructor...
constructor( meta ) {
this.carriers = {};
// which carriers did we get?
Object.keys(meta.carriers).forEach( code =>
this.carriers[code] = new Carrier( code, meta.carriers[code] )
);
In turn, the Carrier class constructor will read the "logos" and "name" properties, either into its fields, which may include even more class instances.
Keep going, until you're done.
If you want to create a tree of Javascript instances of models from JSON, you can map the JSON to the root class constructor like this:
getItiniraries(params: any): Observable<Itinerary> {
return this.http.get<Itinerary>('http://localhost:4202/api/itinirary', {params: params}).pipe(map(x => new Itinerary(x));
}
Then define the following constructor for all of your model classes:
constructor(obj) {
Object.assign(this, obj);
}
Then for all the children of a model class, replace these declarations public meta: Meta; with:
private _meta: Meta;
get meta() {
return this._meta;
}
set meta(value: any) {
this._meta = new Meta(value);
}
Same goes for children as Array:
private _results: Result[];
get results() {
return this._results;
}
set results(value: any) {
this._results = value.map(x => new Result(x));
}
Then your tree of objects will be composed of instances of your model classes, and you'll be able to benefit from the potential functions you'll define (for example data formatting functions, or whatever else)
using new es6 features it is quite easy this._results = value.map(x => new Result(x)); can bethis._results = [...value] this case if value is nested json array it will be flat as you want.

Angular: How to map JSON result from HttpClient request to class instances?

I have the following code which seems wrong:
public search(searchString: string): Observable<Array<ClientSearchResult>> {
let params = new HttpParams().set('searchString', searchString);
return this.http
.get<Array<ClientSearchResult>>(this.searchUrl, { params: params })
.map((results: ClientSearchResult[]) => results.map((r: ClientSearchResult) => new ClientSearchResult(r)));
}
I know that the API is returning a JSON object which is not the same as an instance of my TypeScript class. However, I want to use properties defined in the TypeScript class.
Is there a better way to map the array coming from my API call to an array that actually consists of instances of ClientSearchResult?
Here is the ClientSearchResult object:
import { Name } from './name';
export class ClientSearchResult {
public id: string;
public name: Name;
public dateOfBirth: Date;
public socialSecurityNumber: string;
public get summary(): string {
let result: string = `${this.name}`;
if (this.dateOfBirth)
result += ` | ${this.dateOfBirth.toLocaleDateString()}`;
return result;
}
constructor(source: ClientSearchResult) {
this.id = source.id;
this.name = new Name(source.name);
this.dateOfBirth = source.dateOfBirth? new Date(source.dateOfBirth) : undefined;
this.socialSecurityNumber = source.socialSecurityNumber;
}
public toString(): string {
return this.summary;
}
}
We use a wonderful library to map json to typescript objects.
https://github.com/shakilsiraj/json-object-mapper
json-object-mapper depends on reflect-metadata library as it is using decorators to serialize and deserialize the data.
As an option you may try TypeScript as operator to cast your API response to the ClientSearchResult type.
import { Http, Response } from '#angular/http';
public search(searchString: string): Observable<ClientSearchResult[]> {
const params = new HttpParams().set('searchString', searchString);
return this.http.get(this.searchUrl, { params: params })
.map((results: Response) => results.json() as ClientSearchResult[]);
}
This approach requires your model class to be used as an interface, or just to be an interface:
interface ClientSearchResult {
id: number;
// etc
}
I have been using this very nice (and up-to-date at the time of posting) library:
https://www.npmjs.com/package/class-transformer
It can handle very complex cases with nested classes and more.

How I can get transform Promise to something like a list?

I am new to dealing with Angle 2 I have developed a service REST API that returns me a JSON string. Which makes the request is the following code:
getUsuaris(): Promise<Usuari> {
return this.http.get(this.URLUsuaris)
.toPromise()
.then(response => response.json().data as Usuari[])
.catch(this.handleError);
}
The following code is a simple login, without more, but not because the authenticatedUserList reason is NULL and obiamente its attributes.
private URLUsuaris = 'http://localhost:50541/api/Usuaris';
constructor(
private _logger: Logger,
private _router: Router,
private http: Http) { }
logIn(usuari: Usuari) {
var authenticatedUserList: Promise<Usuari> = this.getUsuaris();
var authenticatedUser: Usuari;
this._logger.log(authenticatedUserList[0]);
for (var i in authenticatedUserList) {
if (authenticatedUserList[i].usuari === usuari.Usuari1) {
authenticatedUser = authenticatedUserList[i];
break;
}
}
if (authenticatedUser && authenticatedUser.Password === usuari.Password){
localStorage.setItem("usuari", authenticatedUser.Usuari1);
this._router.navigate(['Home']);
this._logger.log(localStorage.getItem("usuari"));
this._logger.log('Usuari Registart!');
return true;
}
this._logger.log('Usuari No Registart!');
return false;
}
The server with the REST API returns the information correctly!
User class which is what sends the API:
export class Usuari {
public Id: number;
public Usuari1: string;
public Password: string;
constructor(usuari: string, password: string) { }
}
Please if you have any idea what might be failing an explanation not be obliged to meet the same mistake in the future.
Thank you so much!!
You need to use the return of the promise like this:
private URLUsuaris = 'http://localhost:50541/api/Usuaris';
constructor(
private _logger: Logger,
private _router: Router,
private http: Http) { }
logIn(usuari: Usuari) {
return this.getUsuaris().then((authenticatedUserList)=>{
var authenticatedUser: Usuari;
this._logger.log(authenticatedUserList[0]);
for (var i in authenticatedUserList) {
if (authenticatedUserList[i].usuari === usuari.Usuari1) {
authenticatedUser = authenticatedUserList[i];
break;
}
}
if (authenticatedUser && authenticatedUser.Password === usuari.Password){
localStorage.setItem("usuari", authenticatedUser.Usuari1);
this._router.navigate(['Home']);
this._logger.log(localStorage.getItem("usuari"));
this._logger.log('Usuari Registart!');
return true;
}
this._logger.log('Usuari No Registart!');
return false;
});
}
This also changed the return of the logIn method so that now it returns a Promise<boolean> instead of boolean.