1

I am working on a AngularJS 2 app using Typescript. I am facing a problem where I cannot access a property defined in the Typescript class in the HTML page using data binding. I have included the relevant files below. Please let me know if I am missing something. I am looking at this one issue for more than 2 days now and I am out of ideas at present.

Typescript classes.

export class ApproveBooking {
constructor(
    public name: string,
    public form: DynamoForm,
    public cabBooking: CabBooking) {}
 }

export class DynamoForm {
constructor(
    public formFields: DynamoFormField[]) { }
 }

export class DynamoFormField {
constructor(
    public fieldName: string,
    public fieldId: string,
    public fieldLabel: string,
    public fieldType: string,
    public fieldValue: string,
    public required: boolean) { }
}

export class CabBooking {
constructor(
    public id: number,
    public bookingId: string,
    public status: string,
    public notes: string,
    public user: boolean,
    public travelDateString: string,
    public travelTimeString: string,
    public pickupLocation: string,
    public dropLocation: string,
    public approver: string,
    public approverAction: string,
    public approverComments: string,
    public approvedDate: string,
    public cabDriver: string,
    public cabDriverAssignedDate: string,
    public createdDate: string,
    public modifiedDate: string) { }
}

Service that fetches JSON data from server and assigns it to the 'ApproveBooking' class.

getSingleCabBookingForApproval(bookingId: string) {
    var accessToken = localStorage.getItem('cabservice_access_token');
    var approveUrl = this._appService.getAppUrl() + "/dynamo/api/cabservice/bookings/approve/" + bookingId + "?access_token=" + accessToken;

    return this._http.get(approveUrl)
        .map(res => {
            return <ApproveBooking> res.json();
        })
        .catch(this.handleError);
}

JSON value received from the server is below:

{
"name": "Name set in backend...",
"form": {
"formFields": [
  {
    "fieldName": "approverComments",
    "fieldId": "approverComments",
    "fieldLabel": "Approver Comments",
    "fieldType": "textarea",
    "fieldValue": "",
    "required": false
  },
  {
    "fieldName": "approverAction",
    "fieldId": "approverAction",
    "fieldLabel": "Approve Cab Request ",
    "fieldType": "radio",
    "fieldValue": [
      {
        "label": "Approve",
        "name": "approve",
        "selected": false
      },
      {
        "label": "Reject",
        "name": "reject",
        "selected": false
      }
    ],
    "required": false
  },
  {
    "fieldName": "approvedDate",
    "fieldId": "approvedDate",
    "fieldLabel": "Approved Date",
    "fieldType": "datetime-local",
    "fieldValue": "",
    "required": true
  }
]
},
"cabBooking": {
"id": 19,
"bookingId": "BId_1453269360424",
"status": "New",
"notes": null,
"user": "karthik",
"travelDate": 1453228200000,
"travelDateString": null,
"travelTimeString": null,
"pickupLocation": "velachery",
"dropLocation": "chennai one",
"approver": "sivaram",
"approverAction": null,
"approverComments": null,
"approvedDate": null,
"cabDriver": null,
"cabDriverAssignedDate": null,
"createdDate": 1453269360000,
"modifiedDate": 1453269360000
}
}

AngularJS component using the service method to populate one of its properties.

@Component({
templateUrl: 'app/approver/approver_approvebooking.html'
})
export class ApproveCabBookingComponent implements OnInit {
private errorMessage: string;
private approveBooking: ApproveBooking;

constructor(
    private _logger: Logger,
    private _router: Router,
    private _routeParams: RouteParams,
    private _cabBookingService: CabBookingService) { }

ngOnInit() {
    let bookingId = this._routeParams.get('bookingId');
    this._logger.log("bookingId = " + bookingId);

    this.approveBooking = this._cabBookingService.getSingleCabBookingForApproval(bookingId)
        .subscribe(
            approveBooking => {
                this.approveBooking = approveBooking;
                this._logger.log("this.approveBooking => " + JSON.stringify(this.approveBooking));
            },
            err => {
                this._logger.log("Error =>" + err.status + "," + JSON.stringify(err));
            },
            () => console.log('Approve Cab Booking Entity Fetched!');
            );
}

}

HTML template for the component:

<div class="col-md-12 title-panel">
<h3>Booking Status</h3>
</div>
<div class="col-md-12 content-panel">
<div class="row">
    <div class="col-md-12">
        <h4 class="label-heads">Booking ID</h4>
        <div class="form-value">
            <span>{{approveBooking.cabBooking.bookingId}}</span>
        </div>
    </div>
</div>
</div>

The problem I face is when I try to use the approveBooking's inner property in an HTML view, it throws up an error below.

TypeError: Cannot read property 'bookingId' of undefined in [{{approveBooking.cabBooking.bookingId}} in ApproveCabBookingComponent@8:10]

FYI, when I use a flat JSON data and a flat Typescript class, things are working as expected.

1 Answers1

0

After some research, I found out that the problem was due to the fact that component template (HTML) was rendered even before the JSON data was available to the corresponding component.

The following post was helpful to determine the cause. Angular2: How to load data before rendering the component?

By following the solution provided there, I updated the AngularJS component. It is provided below.

@Component({
templateUrl: 'app/approver/approver_approvebooking.html'
})

export class ApproveCabBookingComponent implements OnInit {
private errorMessage: string;
private approveBooking: ApproveBooking;
private isDataAvailable: boolean;

constructor(
    private _logger: Logger,
    private _router: Router,
    private _routeParams: RouteParams,
    private _cabBookingService: CabBookingService) { 

    this.isDataAvailable = false;
}

ngOnInit() {
    let bookingId = this._routeParams.get('bookingId');
    this._logger.log("bookingId inside ngInit = " + bookingId);

    this.approveBooking = this._cabBookingService.getSingleCabBookingForApproval(bookingId)
        .subscribe(
            approveBooking => {
                this.approveBooking = approveBooking;
                this.isDataAvailable = true;
                this._logger.log("this.approveBooking => " + JSON.stringify(this.approveBooking));
            },
            err => {
                this._logger.log("Error while accessing approveBooking...error, JSONed = " + err.status + "," + JSON.stringify(err));
            },
            () => console.log('Approve Cab Booking Entity Fetched!');
            );
}
}

The corresponding view was updated to use the 'isDataAvailable' property set in the component.

<div *ngIf="isDataAvailable">
    <div class="col-md-12 title-panel">
        <h3>Manage Booking </h3>
    </div>
    <div class="col-md-12 content-panel">
        <form (ngSubmit)="onSubmit()">
            <div class="row">
                <div class="col-md-12">
                    <h4 class="label-heads">Booking ID</h4>
                    <div class="form-value">
                        <span>{{approveBooking.cabBooking.bookingId}}</span>
                    </div>
                </div>
            </div>
        </form>
    </div>
</div>
Community
  • 1
  • 1