Link to home
Start Free TrialLog in
Avatar of derrida
derrida

asked on

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
Avatar of Julian Hansen
Julian Hansen
Flag of South Africa image

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.
Avatar of derrida
derrida

ASKER

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.
Avatar of derrida

ASKER

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
Avatar of derrida

ASKER

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
ASKER CERTIFIED SOLUTION
Avatar of Julian Hansen
Julian Hansen
Flag of South Africa image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of derrida

ASKER

i get:
 EXCEPTION: TypeError: Cannot read property '0' of undefined
Avatar of derrida

ASKER

and since i do in the sunscribe method: () => console.log( this.location  )

i get this added picture.User generated image
Does the exception get reported before or after the AJAX call?
Avatar of derrida

ASKER

not sure actually. i attach the console image
User generated image
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?
Avatar of derrida

ASKER

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

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.
Avatar of derrida

ASKER

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.
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?
Avatar of derrida

ASKER

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

Avatar of derrida

ASKER

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
Avatar of derrida

ASKER

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
Avatar of derrida

ASKER

i attach pics of the console of the results from the php side, maybe you'll see something :
User generated imageUser generated image
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.
Avatar of derrida

ASKER

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

Avatar of derrida

ASKER

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

Thanks - PHP i am just going to link the service to a text file with the JSON you posted above.
Nearly there - can you post your bootstrap script (main.ts)
Avatar of derrida

ASKER

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

Trying to do this in the background so finding things as I go  - where is your AppRouterProvider code - we can turf environment.
Avatar of derrida

ASKER

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
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of derrida

ASKER

the right direction and appreciate the time and efforts
You are welcome.