Reputation: 63
How can one call a function on a Typescript model object in an Ionic HTML template? When I attempt this, it results in the error self.context.$implicit.myFunctionName is not a function
.
I want to display photos from the Flickr API in my Ionic2 app. To build the URL to a Flickr photo, one must concatenate several data fields that are provided in the API response and append a suffix indicating the desired photo resolution. This is done in the getUrl
function in the Photo
model.
src/models/photo.ts
export class Photo {
id: string;
owner: string;
secret: string;
server: string;
farm: number;
title: string;
url: string;
// Get the URL to the photo at the size specified by the suffix.
// s small square 75x75
// q large square 150x150
// t thumbnail, 100 on longest side
// m small, 240 on longest side
// n small, 320 on longest side
// - medium, 500 on longest side ... etc
getUrl(suffix: string) {
return 'https://farm' + this.farm +
'.staticflickr.com/' + this.server +
'/' + this.id + '_' + this.secret + '_' + suffix + '.jpg';
}
}
THe HTML template calls getUrl('q')
for each photo in the returned set.
<div *ngFor="let photo of photos" class="photo-thumbnail">
<img [src]="photo.getUrl('q')" />
</div>
This results in the error
Uncaught Error: Error in ./PhotosPage class PhotosPage - inline template:13:9 caused by: self.context.$implicit.getUrl is not a function
Additional code:
src/pages/photos.ts
import { Component } from '@angular/core';
import { Photo } from '../../models/photo';
import { FlickrService } from '../../services/flickr.service'
@Component({
selector: 'page-photos',
templateUrl: 'photos.html'
})
export class PhotosPage {
photos: Photo[];
constructor(private flickrService: FlickrService) {
// Get photos data from Flickr (this is working as expected)
flickrService.getPhotos().subscribe(photos => {
this.photos = photos;
});
}
}
services/flickr.service.ts
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from 'rxjs/Rx';
import 'rxjs/add/operator/map';
import { Photo } from '../models/photo';
@Injectable()
export class FlickrService {
constructor(public http: Http){}
// Load the first 100 photos for a user (this is working as expected)
getPhotos(): Observable<Photo[]> {
return this.http.get('https://api.flickr.com/path/to/api/call')
.map(res => <Photo[]>res.json().photos.photo);
}
}
Upvotes: 1
Views: 1105
Reputation: 2719
Okay so now you will see where the Javascript language sucks. The problem is that when you get the photos from the server, they are pure json objects (because of course we cannot have functions defined in a JSON that comes from the server right?). So That casting actually doesnt do ANYTHING. After you transpile it gets removed and the only thing why you should put it there is to get the autocomplete/make the project look better so other developer will actually know what is happening. The thing is that your getUrl function is an instance function but the photos in that array are not an instance to photo (the casting doesnt do anything on its own). So now we need some kind of serialization tool which will transform JSON into a Typescript object (and we dont have clean way at the moment). You can do two things:
Iterate the array that you get from the backend, and for each photo create new instance: Ex: var photo = new Photo(url, etc); photos.push(photo) and so on to actually create an instance of the class. After that you will be able to call that method. (not very nice)
Make a static function which you can use anywhere so you will be able to use it like: createUrl(photo) <- the whole photo object. (instead of photo.createUrl()) (best way at the moment)
Find some libraries which are doing the serialization (too complicated)
I hope this will help you.
Typescript power is that you get design time support and not run-time support :(
Upvotes: 1