I'm creating a app usign angular and typescript. Everything is comming together nicely, but one issue bugs me.
I define entity/model classes that i would like to pass around in the app, the data for the classes comes from JSON from $resource calls.
Here is an example of a model class:
module app.domain {
export interface IJob {
id: number;
jobTitle: string;
jobDescription: string;
}
export class Job implements IJob {
constructor(public id:number, public jobTitle: string, public jobDescription: string) {
}
}
}
I access my JSON resource through a service that returns the resource:
namespace app.services {
'use strict';
interface IDataService {
getJobResource(): angular.resource.IResourceClass<IJobResource>
}
interface IJobResource extends angular.resource.IResource<app.domain.IJob>{
}
export class DataService implements IDataService {
static $inject: Array<string> = ['$log','$resource','ApiDataEndpoint'];
constructor(
private $log: angular.ILogService,
private $resource: angular.resource.IResourceService,
private ApiDataEndpoint
){}
getJobResource(): angular.resource.IResourceClass<IJobResource>{
return this.$resource(this.ApiDataEndpoint.url + 'job/:id',{});
}
}
angular
.module('app.services',[])
.service('dataService', DataService);
}
This is where it goes wrong, when I cast the result of the resource call to Ijob typescript restrict me to call properties that don't match the names in the interface (as expected) but because this is still just JSON I'm unable to make method calls and if the properties en the IJob interface do not match the names in the JSON result the I get nothing.
So, my questing is: What is the correct way to implement a service that calls a restfull endpoint that returns JSON and then returns an array of objects or a single object. The way should support that names on JSON and on the objects might not match.
You could make a class that has the functionality you need and gets a IJob object to work with.
module app.domain {
export interface IJob {
id: number;
jobTitle: string;
jobDescription: string;
}
export class Job {
// keep the data received from the server private
constructor(private jobData: IJob) {}
// class methods
public isIdGreaterThanTen(): boolean {
return this.jobData.id > 0;
}
// expose the properties of the IJob interface via getters and setters
public get id(): number {
return this.jobData.id;
}
public set id(id: number): void {
this.jobData.id = id;
}
// so on for all properties if needed
// ...
}
}
In the service you can use the transformResponse functionality of $resource to create and return a new instance of the Job class instead of the object returned by the server.
namespace app.services {
'use strict';
interface IDataService {
getJobResource(): angular.resource.IResourceClass<IJobResource>
}
// use the Job class instead of the interface
interface IJobResource extends angular.resource.IResource<app.domain.Job>{
}
export class DataService implements IDataService {
static $inject: Array<string> = ['$log','$resource','ApiDataEndpoint'];
constructor(
private $log: angular.ILogService,
private $resource: angular.resource.IResourceService,
private ApiDataEndpoint
){}
getJobResource(): angular.resource.IResourceClass<IJobResource>{
return this.$resource(this.ApiDataEndpoint.url + 'job/:id',{},
{
get: {
method: 'GET',
transformResponse: function(data: IJob) {
return new Job(data); // create a new instance of the Job class and return it
}
}
});
}
}
angular
.module('app.services',[])
.service('dataService', DataService);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With