• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 202
  • Last Modified:

angular 2 problem accessing json resutls

hi
so i have a for for edit, that the id is passed as a parameter. then onInit i have this to get the results from that id, and i do get it back from the database.
BUT when i try to access the values in the onInit i can but not in the template:
so i have an interface and i use it at the beginning of the class component:
location:Location;

then onInit i have this:
    this._route.params
      .map(params => params['id'])
      .subscribe((id) => {
        this.title = id ? "Edit Location" : "New Location";
        if (!id) {
          return;
        }
        //console.log(id);
        this._locationService.getLocation(id)
          .subscribe(
              location => this.location = location,
              error => alert(error),
              () => console.log(this.location[0].name)
            );
      });

Open in new window


as you can see, here i can access the name value:  () => console.log(this.location[0].name)

But in the template when i do the same:
{{location[0].name}}

in the console i get: EXCEPTION: TypeError: Cannot read property '0' of undefined

when i do {{location | json}} i get:
[ { "id": "1", "name": "sigliz coffee", "address": "hzel st 36,TA", "lat": "32.060143", "lng": "34.770557" } ]

what am i missing here? and how can i access the name for example?

best regards
0
derrida
Asked:
derrida
  • 18
  • 9
  • 2
2 Solutions
 
Julian HansenCommented:
Do you have a link that we can look at to see the error in context?

It looks like you are doing everything correctly but obviously something is missing as you are getting the error.

Have you tried initialising location - for example.

this.location = [{}]

It might be an async issue where the view is rendering before location has been set to at the time location[0] is not defined - although this should resolve

Are you saying that the view does not display the value or are you asking about the error in the console?

Very difficult to diagnose without seeing the full picture.
0
 
derridaAuthor Commented:
hey
it is on my local machine. but let me try to give you as much as possible.
i have a component with a form that will be used both to add or edit a location.
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, ROUTER_DIRECTIVES, CanDeactivate, Router } from '@angular/router';
import {FormBuilder,ControlGroup,Validators} from '@angular/common';


//import the service
import {LocationService} from '../location.service';
//import the interface
import {Location} from '../shared/location';

@Component({
  moduleId: module.id,
  selector: 'app-locationform',
  templateUrl: 'locationform.component.html',
  styleUrls: ['locationform.component.css'],
  directives: [ROUTER_DIRECTIVES],
  providers: [LocationService]
})
export class LocationformComponent implements OnInit {

  title: string;
  public location:Location;

  form:ControlGroup;

  
  


  constructor(
    private _route: ActivatedRoute,
    private _locationService: LocationService,
    fb:FormBuilder
    ) {
      this.form = fb.group({
        name: ['', Validators.required],
        address: ['', Validators.required],
        lat: ['', Validators.required],
        lng: ['', Validators.required],
      });
      var test = location;

      //alert(this.location);
      //console.log( this.location);

  }

  ngOnInit() {
    //console.log(this._route.params['value']['id']);
    
    this._route.params
      .map(params => params['id'])
      .subscribe((id) => {
        this.title = id ? "Edit User" : "New User";
        if (!id) {
          return;
        }
        //console.log(id);
        this._locationService.getLocation(id)
          .subscribe(
              location => this.location = location,
              error => alert(error),
              () => console.log( typeof this.location  )
            );
      });

      //console.log( typeof this.location);
      
      
  }

}

Open in new window


in my location service i have this method:
  getLocation(locationId){
    let theparams = JSON.stringify({
      action: 'getlocation',
      id: locationId
    });

    let headers = new Headers();
    headers.append('Content-Type','application/x-www-form-urlencoded;charset=utf-8');

    return this._http.post(
      'http://localhost:80/ang2router/public/dbactions.php',
      theparams,
      {headers: headers}
    )
    .map(res => res.json());
  }

Open in new window

this is the method that is called from onInit before.

the php file query the database, and json_encode the results.

 public location:Location; is an interface:
export interface Location{
    name: string;
    address: string;
    lat: number;
    lng: number;
}

Open in new window


so in the template when i do: {{ location | json}}
i get: [ { "id": "1", "name": "sigliz coffee", "address": "hzel st 36,TA", "lat": "32.060143", "lng": "34.770557" } ]

but when i write: {{location.name}}
in the console i get that name is undifined. if i use the Elvis operator the error not shown in the console, but the name is not displying.

hope this is more info for you to help me.
0
 
derridaAuthor Commented:
bizarre this: on the form tag i add: *ngFor="let loc of location" and them if i do {{loc.name}} it works. so we need to loop the returned data i guess
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
derridaAuthor Commented:
jesus christ. if i do the *ngFor i can access the loc.name but if it is an add location form i get nothing and also no error messages in the log. so the my last comment doesn't really help
0
 
Julian HansenCommented:
"...we need to loop the returned data i guess "

You should be able to access the data as you have being - did you try the initialisation suggestion?

What do you get if you do this

{{ location[0] | json}}
0
 
derridaAuthor Commented:
i get:
 EXCEPTION: TypeError: Cannot read property '0' of undefined
0
 
derridaAuthor Commented:
and since i do in the sunscribe method: () => console.log( this.location  )

i get this added picture.the log
0
 
Julian HansenCommented:
Does the exception get reported before or after the AJAX call?
0
 
derridaAuthor Commented:
not sure actually. i attach the console image
console
0
 
Julian HansenCommented:
It does look like this is probably a synching issue

If you initialise location in your constructor to an array like this

this.location = [{
  name: 'No name'
}];

Open in new window


Do you still get the exception?
0
 
derridaAuthor Commented:
i have written this in the constructor:
    this.location = [{
      name: '',
      address: '',
      lat: '',
      lng: ''
    }];

Open in new window

because of the interface.
and before running it, i see:
Type '{ name: string; address: string; lat: string; lng: string; }[]' is not assignable to type 'Location'.
  Property 'name' is missing in type '{ name: string; address: string; lat: string; lng: string; }[]'.
(property) LocationformComponent.location: Location
0
 
BigRatCommented:

when i do {{location | json}} i get:
[ { "id": "1", "name": "sigliz coffee", "address": "hzel st 36,TA", "lat": "32.060143", "lng": "34.770557" } ]

what am i missing here? and how can i access the name for example?

Allows you to convert a JavaScript object into JSON string

https://docs.angularjs.org/api/ng/filter/json

So why the index 0?

location must be a Javascript object. This location.name should give you the name.
0
 
derridaAuthor Commented:
hi
i don't understand your point. i know that location.name should have given me the name, but it doesn't. that is why i asked the question.

i used the json pipe to see what i get.
0
 
BigRatCommented:
I would like you to try :-

   <div ng-repeat="location">
       <div><span>The Name:</span></span>{{name}}</span></div>
   </div>

because IF location is an array, the repeat should produce at least one row.

And can I see the template?
0
 
derridaAuthor Commented:
hi
you did noticed i am talking about angular 2 right?
the template at the moment is just a form and i am trying to populate it with the chosen location data. it will be an update form:

<h1>{{title}}</h1>
<div class="row">
    <div class="col-md-6 well">
        <form [ngFormModel]="form" (ngSubmit)="save()"
        
        >
            <fieldset>
                <legend>
                    Location - {{location | json}}
                    <br>
                    <!--test - {{location?.name}}-->
                </legend>
                <div class="form-group">
                    <label>Name</label>
                    <input  type="text" class="form-control">
                    <div  class="alert alert-danger">
                        Name is required.
                    </div>
                </div>
                <div class="form-group">
                    <label>Address</label>
                    <input  type="text" class="form-control">
                    <div  class="alert alert-danger">
                        Please type a valid address.
                    </div>
                </div>
                <div class="form-group">
                    <label>Lat</label>
                    <input  type="text" class="form-control">
                </div>
                <div class="form-group">
                    <label>Lng</label>
                    <input  type="text" class="form-control">
                </div>
            </fieldset>

            <button [disabled]="!form.valid" type="submit" class="btn btn-primary">
                Save
            </button>
        </form>
    </div>
</div>

Open in new window

0
 
derridaAuthor Commented:
so in the constructor i log
console.log("From the constructor: " + this.location);
and i get undefined

while in the onInit
i get the returned object
0
 
derridaAuthor Commented:
so just for test, i initialize the location variable like so:
  location:Location = {
      name: 'some name',
      address: 'some addredd',
      lat: '45.74837',
      lng: '32.98575'
    };

Open in new window


and tried, and still couldn't access it in the template. so i commented the oninit part of calling the service and database, and then i do get access to my initialized values.

but it is bizzare since i log the this.location in the complete subscribe method and it is there, and as we know in the template when i use the json pipe it show it.

i'm lost
0
 
derridaAuthor Commented:
i attach pics of the console of the results from the php side, maybe you'll see something :
onetwo
0
 
Julian HansenCommented:
Can you post your code for LocationService and Location - I am intrigued - I want to setup a test project here to try and replicate the problem.
0
 
derridaAuthor Commented:
hi sure.
this is my locationService code:
import { Injectable } from '@angular/core';
import {Http,Headers} from '@angular/http';
import { Observable }     from 'rxjs/Observable';
//import the interface
import {Location} from './shared/location';

@Injectable()
export class LocationService {

  constructor(private _http:Http) {}

  getLocations():Observable<Location[]>{
    let theparams = JSON.stringify({
      action: 'getall'
    });

    let headers = new Headers();
    headers.append('Content-Type','application/x-www-form-urlencoded;charset=utf-8');

    return this._http.post(
      'http://localhost:80/ang2router/public/dbactions.php',
      theparams,
      {headers: headers}
    )
    .map(res => res.json());
  }

  addLocation(){

  }

  deleteLocation(locationId){
    let theparams = JSON.stringify({
      action: 'delete',
      id: locationId
    });

    let headers = new Headers();
    headers.append('Content-Type','application/x-www-form-urlencoded;charset=utf-8');

    return this._http.post(
      'http://localhost:80/ang2router/public/dbactions.php',
      theparams,
      {headers: headers}
    )
    .map(res => res.json());
  }


  getLocation(locationId){
    let theparams = JSON.stringify({
      action: 'getlocation',
      id: locationId
    });

    let headers = new Headers();
    headers.append('Content-Type','application/x-www-form-urlencoded;charset=utf-8');

    return this._http.post(
      'http://localhost:80/ang2router/public/dbactions.php',
      theparams,
      {headers: headers}
    )
    .map(res => res.json());
      
  }


  searchLocation(){

  }

}

Open in new window


and the location interface:
export interface Location{
    name:string;
    address:string;
    lat:string;
    lng:string;
}

Open in new window

0
 
derridaAuthor Commented:
and maybe even the php side, even though this is just the basic queries and nothing more:
<?php
header("Access-Control-Allow-Origin: *");
header('Access-Control-Allow-Headers: X-Requested-With');
header('Content-Type: application/json');
$obj = json_decode(key($_POST));
//var_dump($obj);
 // set up the connection variables
        $db_name  = 'locations';
        $hostname = 'localhost';
        $username = 'myusername';
        $password = 'mypass';

        // connect to the database
        $dbh = new PDO("mysql:host=$hostname;dbname=$db_name", $username, $password);


switch ($obj->action) {
    case 'getall':
            // a query get all the records from the users table
            $sql = 'SELECT * FROM markers';

            // use prepared statements, even if not strictly required is good practice
            $stmt = $dbh->prepare( $sql );

            // execute the query
            $stmt->execute();

            // fetch the results into an array
            $result = $stmt->fetchAll( PDO::FETCH_ASSOC );
                                                        
            echo json_encode($result);
        break;
    case 'add':
            // a query get all the records from the users table
            $sql = 'INSERT INTO markers ( name, address,lat, lng ) 
                VALUES ( "' . $obj->name .'" , "'.$obj->address.'", "'.$obj->lat.'" , "'.$obj->lng.'" );';

            // use prepared statements, even if not strictly required is good practice
            $stmt = $dbh->prepare( $sql );

            // execute the query
            $check = $stmt->execute();

            $last_id = $dbh->lastInsertId();

            $sql2 = "SELECT * FROM markers WHERE id=$last_id ";

            // use prepared statements, even if not strictly required is good practice
            $stmt = $dbh->prepare( $sql2 );

            // execute the query
            $stmt->execute();

            // fetch the results into an array
            $result = $stmt->fetchAll( PDO::FETCH_ASSOC );

                                                                
            echo json_encode($result);


        break;
    case 'delete':
   
            // a query get all the records from the users table
            $sql = "DELETE FROM markers WHERE id=" . $obj->id;

            // use prepared statements, even if not strictly required is good practice
            $stmt = $dbh->prepare( $sql );

            // execute the query
            $check = $stmt->execute();

            if ($check) {
                $result = "Deleted successfully";
            }else{
                $result = "something went wrong";
            }

                                                                
            echo json_encode($result);
        break;

    case 'getlocation':
     
            // a query get the selected location
            $sql = "SELECT * FROM markers WHERE id=" . $obj->id;
            //var_dump($sql);

            // use prepared statements, even if not strictly required is good practice
            $stmt = $dbh->prepare( $sql );

            // execute the query
            $stmt->execute();

            // fetch the results into an array
            $result = $stmt->fetchAll( PDO::FETCH_ASSOC );
        
                                                        
            echo json_encode($result);
        break;


    default:
        echo "this is default";
        break;
}

Open in new window

0
 
Julian HansenCommented:
Thanks - PHP i am just going to link the service to a text file with the JSON you posted above.
0
 
Julian HansenCommented:
Nearly there - can you post your bootstrap script (main.ts)
0
 
derridaAuthor Commented:
sure:
import { bootstrap } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { AppComponent, environment, AppRouterProvider } from './app/';
import {HTTP_PROVIDERS} from '@angular/http';

if (environment.production) {
  enableProdMode();
}

bootstrap(AppComponent, [AppRouterProvider,HTTP_PROVIDERS]);

Open in new window

0
 
Julian HansenCommented:
Trying to do this in the background so finding things as I go  - where is your AppRouterProvider code - we can turf environment.
0
 
derridaAuthor Commented:
in a file called: routes.ts:
import {provideRouter, RouterConfig} from '@angular/router';

import {FirstpageComponent} from './firstpage/firstpage.component';
import {SecondpageComponent} from './secondpage/secondpage.component';
import {ThirdpageComponent} from './thirdpage/thirdpage.component';
import {FourthpageComponent} from './fourthpage/fourthpage.component';
import {Httptest2Component} from './httptest2/httptest2.component';
import {AnimationsComponent} from './animations/animations.component';

import {LocationformComponent} from './locationform/locationform.component';




export const appRoutes: RouterConfig = [
	{path: 'first', component: FirstpageComponent},
	{path: 'second', component: SecondpageComponent},
	{path: 'third', component: ThirdpageComponent},
	{path: 'fourth', component: FourthpageComponent},
	{path: 'httptest2', component: Httptest2Component},
	{path: 'animations', component: AnimationsComponent},
	{path: 'locationform/new', component: LocationformComponent},
	{path: 'locationform/:id', component: LocationformComponent},
	{path: '', redirectTo: 'first'}
];

export const AppRouterProvider = provideRouter(appRoutes);

Open in new window


notice the expoert
0
 
derridaAuthor Commented:
ok think i managed to understand.
two things bothered me: there are 2 subscriptions and accessing the returned data.

and i went back to the answers here:
julian you suggested : {{ location[0] | json}}
and i got and still get: EXCEPTION: TypeError: Cannot read property '0' of undefined

but i tried the [0] not in the templete but on the returned data, there it worked.

and also as i said it bothered me the double subscriptions, so i ended up doing this:
in the onInit:
    let theid = this._route.params['value']['id'];
    this.getTheLoc(theid);

Open in new window

and the getTheLoc method:
  getTheLoc(id){
        this.title = id ? "Edit Location" : "New Location";
        if (!id) {
          return;
        }else{
        //console.log(id);
        this.subscription = this._locationService.getLocation(id)
          .subscribe(
              location => this.location = location[0],
              error => alert(error),
              () => console.log(this.location)
            );
        }
      
  }

Open in new window


notice the  location[0]. if i put it here i get no errors and accessing location.name without issues.
0
 
derridaAuthor Commented:
the right direction and appreciate the time and efforts
0
 
Julian HansenCommented:
You are welcome.
0

Featured Post

Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

  • 18
  • 9
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now