Restangular with Typescript confusing when 'restangularized' response object - angularjs

I am using Angular 1.5.x with TypeScript. For accessing a remote API I use restangular. As a summary this is my scenario:
My API has the following resource http://localhost:53384/api/timezones. Sending a request with the verb GET to that url returns a JSON array:
[
{
"code":"Dateline Standard Time",
"name":"(UTC-12:00) International Date Line West"
},
{
"code":"UTC-11",
"name":"(UTC-11:00) Coordinated Universal Time-11"
},
{
"code":"Hawaiian Standard Time",
"name":"(UTC-10:00) Hawaii"
}
]
Now in my client AngularJs application with TypeScript:
Restangular configuration being restangularProvider: restangular.IProvider
restangularProvider.setBaseUrl("http://localhost:53384/api");
The TimeZone object representation in the client side with typescript
module app.blocks {
"use strict";
export class TimeZone {
public code: string;
public name: string;
}
}
Factory(restangular.IService) to wrap the restangular all 'timezones' resource
module app.services {
factory.$inject = ["Restangular"];
function factory(restangular: restangular.IService): restangular.IElement {
return restangular.all("timezones");
}
angular
.module("app.services")
.factory("app.services.TimeZonesRestangular", factory);
}
Service that uses TimeZonesRestangular to wrap its restangular functionality and return chained promises to whoever requests timezones in an asynchronous way
module app.services {
"use strict";
export interface IStaticDataService {
getTimeZones(): ng.IPromise<app.blocks.TimeZone[]>;
}
class StaticDataService implements IStaticDataService {
constructor(private timeZonesRestangular: restangular.IElement) {
}
public getTimeZones(): ng.IPromise<blocks.TimeZone[]> {
return this.timeZonesRestangular.getList()
.then((timeZones: blocks.TimeZone[]) => {
return timeZones;
}, (restangularError: any) => {
throw "Error retrieving time zones. Status: " + restangularError.status;
});
}
}
factory.$inject = ["app.services.TimeZonesRestangular"];
function factory(timeZonesRestangular: restangular.IElement): IStaticDataService {
return new StaticDataService(timeZonesRestangular);
}
angular
.module("app.services")
.factory("app.services.StaticDataService", factory);
}
And finally in the controller using the service to get the 'timezones' asynchronously I have this statement
//..other controller things not relevant for this sample
this.staticDataService.getTimeZones()
.then((timeZones: blocks.TimeZone[]) => {
this.timeZones = timeZones;
});
There are 2 PROBLEMS:
The type definition for restangular (which I installed with tsd install restangular --resolve --save) tells me that the successCallback in the getTimeZones() method is a promiseValue: any[], which is fine because it is indeed an array. I thought it would be an array of TimeZone[] and typescript compiles properly because it accepts any[], but when debuggin I see that the successCallback promised value it's not an array of TimeZone[]. It has the properties I expected (code and name) but it also has many other things restangular-ish. An object within that array looks like this (plus some functions):
{
"code":"Dateline Standard Time",
"name":"(UTC-12:00) International Date Line West",
"route":"timezones",
"reqParams":null,
"restangularized":true,
"fromServer":true,
"parentResource":null,
"restangularCollection":false
}
As per https://github.com/mgonto/restangular/issues/150 it looks as if my response had been "restangularized". Scary description for somebody new to restangular like myself..
What interface in restangular type definition should I use to represent the array of restangularized TimeZone[] ?
Is there any example on how to achieve something similar with TypeScript?
Thank you.

After digging a little bit further I found out that the best way to handle this is by expecting a promised value of type restangular.ICollection (which inherits from IService and Array<any>) so that I can de-restangularize the response like this:
public getTimeZones(): ng.IPromise<blocks.TimeZone[]> {
return this.timeZonesRestangular.getList()
.then((restangularizedTimeZones: restangular.ICollection) => {
return restangularizedTimeZones.plain();
}, (restangularError: any) => {
throw "Error retrieving time zones. Status: " + restangularError.status;
});
}
Now everthing seems to be fine and the response is, indeed, a promise of TimeZone[]

Related

How do I use the factory pattern if I'm migrating from Angular 1.5.8 to Angular 2?

If I'm migrating to Angular 2 and I want to use the factory pattern to create a transient dependency (a dependency that doesn't share state between components in which it gets injected), what is the best way to register a service in angular 1.5.8 with plans to migrate to ng2's way of registering services
I know in angular 2, components can reinstantiate services by passing them to the providers array, but that option doesn't exist in angular 1, and it seems like .factory is all but deprecated in angular 2 since the docs don't give it much love
An example would be a TabulationService that manages the state of which tab a user is viewing on a dashboard, which would obviously not share state between components. I would want to reinstantiate the service in each component it gets injected into. But I also want to avoid using .factory if Angular 2 best practices seem to shy away from using it.
Here is the "hack" I've resorted to, but I don't like it because even though it gets me type hinting and statelessness for my service, I can't use dependency injection in the object that gets created and i have to manage the state of my service when it gets injected and when the component in which it gets injected is destroyed (by manually clearing the service's state):
tab-manager.service.ts:
import { TabManager } from './tab-manager.class';
export class TabService {
manager;
public initialize(tabs: string[], defaultTab: string) {
this.manager = new TabManager(tabs, defaultTab);
}
}
tab-manager.class.ts:
import { includes } from 'lodash';
const mandatory = (param) => { throw new Error(`${ param } is a required field in Tab Manager!`) };
export class TabManager {
tab: string;
constructor(public tabs: string[] = mandatory(`tabs`), public defaultTab: string = mandatory('defaultTab')) {
this.checkTab(defaultTab);
this.tab = defaultTab;
}
public switchTab(tab) {
const self = this;
self.checkTab(tab);
self.tab = tab;
}
private checkTab(tab: string) {
const self = this;
if (!includes(self.tabs, tab)) {
throw new Error(`{ tab } is not a valid tab. Available tabs are ${ self.tabs.join(',') }`);
}
}
}
The service then gets initialized by importing the TabManager service from tab-manager.service.ts and calling `angular.service('TabService', TabManagerService)
There's nothing to blame Angular 2 documentation on. Angular 1 factory service is implemented in Angular 2 DI as useFactory provider.
It doesn't really matter here if it is factory or service in this case. Both serve the same purpose and share the same behaviour - they are singletons in Angular 1. Similarly, useFactory and useClass providers are singletons within the same injector in Angular 2.
To achieve the desired behaviour uniformly in both frameworks a dependency should be instantiated after injection. It doesn't really matter if this is done with factory or constructor function - they should be defined as value service in Angular 1 or useValue provider in Angular 2.
For Angular 1 it will be:
export class TabulationService { ... }
export type TTabulationService = typeof TabulationService;
...
app.value('TabulationService', TabulationService);
app.component('some', {
controller: class {
tabulationService: TabulationService;
static $inject = ['TabulationService'];
constructor (TabulationService: TTabulationService) {
this.tabulationService = new TabulationService();
}
}
});
And for Angular 2:
providers: [{ provide: TabulationService, useValue: TabulationService }]
...
#Component(...)
export class SomeComponent {
tabulationService: TabulationService;
constructor (#Inject(TabulationService) TabulationService: TTabulationService) {
this.tabulationService = new TabulationService();
}
}

Angular2 RxJS calling class function from map function

I'm new to Angular 2 and Observables so I apologise if my problem is trivial. Anyway I'm trying to test the Angular 2 HTTP Client using RxJS. Although I got it to work I need to add more logic to the service I'm currently working on. Basically I'd like to have a mapping function to convert the object I receive from the web service I'm connected to, to the model object I have in Angular.
This is the code that works:
import { Injectable } from 'angular2/core';
import { Http, Response } from 'angular2/http';
import { Observable } from 'rxjs/Observable';
import { Person } from '../models/person';
#Injectable()
export class PersonsService {
constructor(private http: Http) { }
private personsUrl = 'http://localhost/api/persons';
getPersons(): Observable<Person[]> {
return this.http.get(this.personsUrl)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
if(res.status < 200 || res.status >= 300) {
throw new Error('Bad response status ' + res.status);
}
let body = res.json();
return body.data || {};
}
private handleError(error: any) {
let errMsg = error.message;
return Observable.throw(errMsg);
}
}
With the above code I have no problems whatsoever. The issue I'm having is that I'd like to map the object I'm getting from the service to the one I have in Angular i.e. Person. What I tried is to call another function from the extractData function that's being used by the .map function.
private extractData(res: Response) {
if(res.status < 200 || res.status >= 300) {
throw new Error('Bad response status ' + res.status);
}
let body = res.json();
// map data function
var data = this.mapData(body.data);
return data || {};
}
private mapData(data: any) {
// code to map data
}
Obviously the code above doesn't work as when this is referenced inside the extractData function, this does not refer to the PersonsService class, but it refers to a MapSubscriber object.
I don't know if it is possible to call an "external" function. It might be a silly thing but I can't find any information regarding this.
Instead of just passing the function reference use arrow functions to retain this
.map((res) => this.extractData(res))
Observable's map function allows you to pass a reference variable as a second argument on how should this actually work inside the higher-order function.
so the solution is
.map(this.extractData,this)
This way while passing the extractData function you are also passing the current class's this execution context to the higher-order function.
It will work.
Observable Doc Reference Link

Using $http interceptor with custom fields in a typesafe manner in angular + typescript

I currently implemented an angular $http interceptor adding custom headers to requests based on a localstorage value (I need to implement a "Su" feature in my app)
I need to "deactivate" this behaviour on some special requests (=I need to be able to configure this on a per-request basis), and I'd like to do this by putting an extra config parameter denoting this when calling my $http methods.
The interceptor is looking like this :
$httpProvider.interceptors.push((localStorageService: ng.local.storage.ILocalStorageService) => {
return {
request: (config: ng.IRequestShortcutConfig) => {
var su = localStorageService.get<string>('Su');
if(su && !("avoidSudo" in config)){
config.headers.Su = `{ "principal": "${su}" }`;
}
return config;
}
}
});
And the $http service call is looking like this when I want to deactivate my "su" feature :
this.$http.get('/api/sessions/current', { avoidSudo: true })
In typescript 1.6, this code doesn't compile as $http.get()'s second argument is expected to be a ng.IRequestShortcutConfig which obviously doesn't contain my specific avoidSudo field (Typescript compilation error is perfectly right here)
I can workaround the compilation error by replacing the { avoidSudo: true } by <any>{ avoidSudo: true } but this is clearly not ideal in terms of typesafety
I tried to create a new SkipSudoRequestShortcutConfig dedicated class (implementing ng.IRequestShortcutConfig) for this purpose. Something like this :
module mymodule {
export class SkipSudoRequestShortcutConfig implements ng.IRequestShortcutConfig {
// For testing purposes only
_noSudo: boolean;
constructor(
public params?: any,
public headers?: any,
public xsrfHeaderName?: string,
public xsrfCookieName?: string,
public cache?: any,
public withCredentials?: boolean,
public data?: any,
public transformRequest?: any,
public transformResponse?: any,
public timeout?: any,
public responseType?: string
){
this._noSudo = true;
}
}
}
called like this :
var config = new SkipSudoRequestShortcutConfig();
console.log(config instanceof SkipSudoRequestShortcutConfig); // Shows true
console.log(config._noSudo); // Shows true
this.$http.get('/api/sessions/current', config)
and used like this in the interceptor :
request: (config: ng.IRequestShortcutConfig) => {
var su = localStorageService.get<string>('Su');
console.log(config instanceof SkipSudoRequestShortcutConfig); // Shows *false* :(
// console.log(config._noSudo); // Doesn't compile, but if executed at runtime with a breakpoint, it works and display true
if(su && !(config instanceof mymodule.SkipSudoRequestShortcutConfig)){
config.headers.Su = `{ "principal": "${su}" }`;
}
return config;
}
but once in the request handler, the instanceof test was falsy.
I was wondering what would be the best/simplest way to achieve this goal.
Do you think the ng.IRequestShortcutConfig is missing a special config field allowing to put custom fields from $http invocations to interceptor handlers ?
Thanks in advance,
It's important to remember that types in TypeScript are just "type helpers" and removed when transpiled to javascript. So you don't need to implement a new $http service. Instead you can just create a new type that suites your needs.
In reality it's because the angular type definition is lacking.
You can fix this by creating the following interfaces.
export interface IRequestShortcutConfigWithCustomConfig extends ng.IRequestShortcutConfig {
[key: string]: any;
}
I'm using a dictionary style type cause that'll support everything. But you could change that to avoidSudo: boolean; if you don't want a general definition.
export interface IHttpServiceWithCustomConfig extends ng.IHttpService {
get<T>(url: string, config?: IRequestShortcutConfigWithCustomConfig): ng.IHttpPromise<T>;
}
Then in your controller you just use the new interface.
constructor(private $http: IHttpServiceWithCustomConfig) {
$http.get("/api/sessions/current", { avoidSudo: true }
}
You do the exact same with IHttpInterceptor
export interface IRequestConfigWithCustomConfig extends ng.IRequestConfig, IRequestShortcutConfigWithCustomConfig {
}
export interface IHttpInterceptorWithCustomConfig extends ng.IHttpInterceptor {
request?: (config: IRequestConfigWithCustomConfig) => IRequestConfigWithCustomConfig | ng.IPromise<IRequestConfigWithCustomConfig>;
}

Decorating angular exceptionHandler service in Typescript using VS 2013

Using Typescript and Visual Studio 2013
I want to decorate the angularjs $exceptionHandler and I'm not quite sure of the correct way to do it. I have decorated $log and it works fine.
My configuration:
MyAngularModule.config(['$provide',($provide) => {
$provide.decorator('$log', ['$delegate', function ($delegate) {
var myLog: MyLogService.LogService = new MyLogService.LogService();
return myLog;
}]);
}]);
MyLogService.ts
export = Example;
module Example {
export class LogService implements ng.ILogService{
constructor() {}
public assertEmpty = () => { };
public reset = () => { };
public info: ng.ILogCall = ...all my code...
}
}
Now when I try doing a similar thing with $exceptionHandler:
$provide.decorator('$log', ['$delegate', function ($delegate) {
var myExHandler: MyExHandler.ExHandlerService = new MyExHandler.ExHandlerService();
return myExHandler;
}]);
The interface definition for exceptionHandler in the type files is:
interface IExceptionHandlerService {
(exception: Error, cause?: string): void;
}
How do I code this similar to the way I did in decorating the log service?
I tried:
export = Example;
module Example {
export class ExHandlerService implements ng.IExceptionHandlerService{
constructor(anException: Error, aCause?: string) {}
}
}
And Visual Studio is complaining that the interface is implemented wrong. I don't really understand what (exception: Error, cause?: string): void; means, is it the constructor? The typings file does show a name, just this anonymous method definition.
I don't understand how I even approach this in the decorate call (ie. where do the error and cause parameters come from and how do I keep the default implementation operating as well?)
Thanks in advance.
Update:
Considering in angular.d.ts:
interface IExceptionHandlerService {
(exception: Error, cause?: string): void;
}
I guess my root confusion is how this interface would be implemented in Typescript??
Ok, I found that interfaces can define a function type in this case. That created confusion with the way I have viewed interfaces typically. It seems I can't use an "implements" in this case?
I think I've found my own answer. In any case here is how I decorated the angular exception handler service in Typescript.
export class MyExceptionHandlerService {
private _defaultExceptionHandlerService: ng.IExceptionHandlerService;
constructor(paramDelegate: ng.IExceptionHandlerService) {
this._defaultExceptionHandlerService = paramDelegate;
}
public exception: ng.IExceptionHandlerService = (paramException: Error, paramCause?: string): void => {
console.error("MY ERROR HANDLER: " + paramException.message)
this._defaultExceptionHandlerService(paramException, paramCause);
}
}
And I use it from my angular config section like this:
$provide.decorator('$exceptionHandler', ['$delegate', function ($delegate: ng.IExceptionHandlerService) {
var localExceptionHandler: MyHandler.ExceptionHandlerService = new MyHandler.ExceptionHandlerService($delegate);
return localExceptionHandler.exception;
}]);
It's working as expected for me.
You could have also done something like this without using objects...not sure if this is good or not. Feedback would be awesome!
((): void => {
'use strict';
angular
.module('app.blocks')
.config(config);
config.$inject = ['$provide'];
function config($provide: ng.auto.IProvideService): void {
$provide.decorator('$exceptionHandler', extendExceptionHandler);
}
extendExceptionHandler.$inject = ['$delegate', 'app.blocks.logger'];
function extendExceptionHandler(
$delegate: any,
logger: app.blocks.ILogFactory): Function {
return (exception: Error, cause?: string, source? : string) => {
$delegate(exception, cause);
logger.log(exception.message, { exception: exception, cause: cause }, source, 'error');
}
}
})();

inject breeze service in angularjs + typescript

class Metadata {
constructor(private breeze) {
this.breeze.? // Ctrl+Space Nothing
breeze.? // Ctrl+Space Everything
}
}
angular.module('bs.breeze', ['breeze.angular']);
// do nothing but you could
// create the window.breeze object
angular.module('bs.breeze').run(['breeze', function (breeze) { }]);
//get breeze metadata by hand
angular.module("bs.breeze").factory('metadata', [ 'breeze', (breeze) => new Metadata(breeze)]);
this.breeze.? shows nothing because "private breeze" has none declared type as you can see.
breeze.? shows everything because it does reference to module breeze declared in breeze.d.ts
breeze and this.breeze are the same object
My problem here is how can use AngularJs standards injections like I'm doing in last line when I'm injecting breeze service into metadata service and then when I'm coding Metadata class how can I use "this.breeze" and have TypeScript advantages like IntelliSense.
Finally, it's possible that this.breeze can be seen as breeze module for IntelliSense purposes or exists other way to implement this scenario?.
You can use typeof to refer to the type of a module. In this case since you have the parameter named breeze, you'll need to either rename the parameter or create an import for the global symbol breeze so you can actually refer to it:
import br = breeze;
class MyClass1 {
constructor(private breeze: typeof br) {
}
}
/*** or ***/
class MyClass2 {
constructor(private br: typeof breeze) {
}
}

Resources