AngularJS automapper for result from restful service - angularjs

I use $resource service for data access from restful service. Result of $resource("path") I put in typescript domain object. Problem is that I get json in form like this:
{
"name_surname": "john_smith",
"years_of_employment": "10"
}
and I want to map it to domain object of this class:
class Employee {
constructor(public FullName: string, public YearsOfEmployment: number) { }
}
So there is mismatch between names of properties inside domain class and json fields. Is there any angularjs module I can use for mapping between those two, and what is the most elegant way to accomplish that?

I am using this in one of my projects.
It maps my shortened properties to readable properties for development.
Maybe this snippet can help you.
(function (angular) {
"use strict";
angular.module("services.mapper", []).service("mapper", [function () {
var models = {
employeeModelContract: {
name_surname: "FullName",
years_of_employment: "YearsOfEmployment"
}
};
return {
map: map,
models: models
}
function map(smallObject, contract) {
var largeObject = {};
for (var smallProperty in contract) {
if (contract.hasOwnProperty(smallProperty)) {
largeObject[contract[smallProperty]] = smallObject[smallProperty];
}
}
return largeObject;
}
}]);
})(angular);
Usage:
var mappedObject = mapper.map(yourJson, mapper.models.employeeModelContract);
UPDATE 1 (Typescript version):
class MapperService implements ng.IServiceProvider {
employeeModelContract:Object= {
name_surname: "FullName",
years_of_employment: "YearsOfEmployment"
};
$get() {
return this;
}
map(smallObject, contract): Object {
var mappedObject: Object = {};
Object.keys(contract).forEach(contractProperty => {
if (contract.hasOwnProperty(contractProperty)) {
mappedObject[contract[contractProperty]] = smallObject[contractProperty];
}
});
return mappedObject;
}
}
class Employee {
constructor(public FullName: string, public YearsOfEmployment: number) { }
}
class Controller implements ng.IController {
constructor(private mapper : MapperService){}
static $inject = ["MapperService"];
json: Object = {
"name_surname": "john_smith",
"years_of_employment": "10"
}
$onInit(): void {
var mapped = <Employee>this.mapper.map(this.json, this.mapper.employeeModelContract);
}
}
angular.module("mapper", []).controller("Controller",Controller).provider("MapperService", MapperService);

Related

Injecting filter into unit test Angular Typescript

I am having trouble injecting the Angular built-in orderByFilter. Any suggestion would be helpful. Thanks in advance! I am getting this error:
TypeError: undefined is not a constructor (evaluating 'this.orderBy(this.registrationList, this.registrationSort, this.sortReverse)
Here is my component:
import { RegistrationModel } from "../models/RegistrationModel";
import { IRegistration } from "../../../services/interfaces/IRegistration";
export class RegistrationsComponent implements ng.IComponentOptions {
public static componentName = "registrations";
public template: string = require("./Registrations");
public controller = RegistrationsController;
}
export class RegistrationsController {
static $inject = ["RegistrationModel", "orderByFilter"];
public registrationsList: IRegistration[] = [];
public registrationSort: string;
public sortReverse: boolean = false;
constructor(
public RegistrationModel: RegistrationModel,
public orderBy
) {}
public sortRegistrations() {
this.registrationsList = this.orderBy(this.registrationsList, this.registrationSort, this.sortReverse);
}
}
Here is my unit test:
import "angular-mocks";
import { RegistrationsComponent, RegistrationsController } from "./RegistrationsComponent";
import { IRegistration } from "../../../services/interfaces/IRegistration";
describe("Registrations", () => {
var registrationModelMock = {};
var orderBy;
beforeEach(angular.mock.module("onboardingTestApp", ($provide: any) => {
$provide.service("RegistrationModel", () => registrationModelMock);
$provide.service("orderByFilter", () => orderBy);
}));
var registrationList = [
{
Created: "2016-05-13",
Email: "test1#test.com",
},
{
Created: "2017-03-13",
Email: "test2#test.com",
},
]
var registrationController = new RegistrationsController(<any>registrationModelMock, orderBy);
it("should sort the registrations", () => {
registrationController.registrationsList = <any>registrationList;
registrationController.registrationSort = "Email";
registrationController.sortReverse = true;
registrationController.sortRegistrations();
expect(registrationList[0].Email).toBe("test2#test.com");
});
});
There is no 'orderBy' pipe in angular 2. They do have other pipes like currency, uppercase, but they removed orderBy.
They removed because of the performance issue from previous angular version.
Here is the the documentation that states that. https://angular.io/docs/ts/latest/guide/pipes.html
Read the appendix part that explains lot more about this.

Inject service in controller with TypeScript in AngularJS not working

i'll try to inject a service in my controller with TypeScript and angular JS. But not working. What's is the problem in my code ? It's my service the problem ? All function in my service is not accessible, all are undefined when i'll try in debug. There is my code.
Controller
module Controller {
import Service = Services;
export class UserCtrl implements Interface.IUserCtrl {
private user: Model.User;
private users: Model.User[];
private userService: Service.UserService;
static $inject = ['$scope','UserService'];
constructor(userService: Service.UserService) {
this.userService = userService;
}
public getUsers = () => {
this.users = this.userService.findAll();
return this.users;
}
public getUser = (name: string) => {
return this.userService.find(name);
}
}
app.controller('UserCtrl', Controller.UserCtrl);
}
Service
module Services {
export class UserService implements Interface.IUserService {
private users: Model.User[];
constructor() {
this.users = [new Model.User("Giunta", "Lucas", 26), new Model.User("Rousselet", "CĂ©line", 26)];
}
public create = (user: Model.User) => {
}
public edit = (user: Model.User) => {
}
public remove = (user: Model.User) => {
}
public find = (name: string) => {
for (let u of this.users) {
if (u.name == name) {
return u;
}
}
}
public findAll = () => {
return this.users;
}
}
app.service('UserService', Services.UserService);
}
It's because you request both $scope and the UserService, but the constructor only accepts the later.
To fix it you should have the properties in the constructor in the same order as the ones in $inject.
static $inject = ['$scope','UserService'];
constructor($scope, userService: Service.UserService) {
this.userService = userService;
}
As #toskv mentioned, you need to also inject $scope into your constructor.
The dependencies are in order, so with the way your code is now, the injector thinks that your injected UserService is $scope which is not what you want.

Generic type name injection

I'm writing my angular app in typescript.
For sake of redundancy prevention I would like to accomplish some type of generic handling.
This is where I'm coming from:
class BaseProvider {
api_url = 'http://localhost:80/api/FILL_OUT_PATH/:id';
$get($resource){
var provider = $resource(this.api_url, {}, {
update: {
method: 'PUT'
}
});
return provider;
}
}
and
class User extends BaseProvider{
constructor() {
super();
this.api_url = 'http://localhost:80/api/users/:id';
}
}
then
module Controllers
{
export class BaseController {
message = "Base controller";
entity : any;
entities : any;
constructor($scope)
{
}
}
}
and
module Controllers
{
export class UserController extends BaseController {
name = "UserController";
constructor($scope, User)
{
super($scope);
this.entity = new User();
this.entities = User.query();
$scope.vm = this;
}
}
}
This is where I'd like to go with UserController (P-Code):
module Controllers
{
export class UserController<T extends BaseProvider> extends BaseController {
name = "UserController";
static $inject = ['$scope', T.typename]; // Inject the types name somehow?
constructor($scope, entity)
{
super($scope);
this.entity = new T();
this.entities = T.query();
$scope.user = this;
}
}
Is there a facility in typescript to handle this?
Is there a facility in typescript to handle this?
No. All type information is erased from the generated JS so you cannot use generic parameters as variables.
There is no way to do it with generics since there will be no typing related information at the runtime, but as a workaround you can pass the type via constructor and type safety with generics. The following code compiles without error and shows how it could be accomplished.
class C1 {
m() { }
}
class C2 extends C1 { }
class C<T extends C1> {
constructor(t: { new (): C1 }) {
var instance = new t();
}
}
new C(C2);

Can I create a TypeScript class within a function and refer to its parameters?

E.g. in angularJS I may use the following construction:
myApp.factory('MyFactory', function(injectable) {
return function(param) {
this.saySomething = function() {
alert("Param=" + param + " injectable=" +injectable);
}
};
});
This can later be used like this:
function(MyFactory) {
new MyFactory().saySomething();
}
When the function passed to the method factory gets invoked, the param injectable is caged and will further be available to new instances of MyFactory without any need to specify that parameter again.
Now I want to use TypeScript and obviously I want to specify that my MyFactory is newable, and has a function saySomething. How could I do this elegantly?
I could write something like this:
class MyFactory {
constructor(private injectable, private param) {}
saySomething() {
alert(...);
}
}
myApp.factory('myFactory', function(injectable) {
return function(param) {
return new MyFactory(injectable, param);
}
});
But this changes the API:
function(myFactory) {
myFactory().saySomething();
}
I wonder if it could be more elegant, because I like how the "new" expresses quite clearly that a new unique object is created and this object creation is the whole purpose of the factory.
** Edit: TypeScript >= 1.6 supports class expressions and you can now write things like:
myApp.factory(injectable: SomeService) {
class TodoItem {
...
}
}
** Original answer:
I have the same problem: with AngularJS and ES5, I enjoy dependency injection not polluting constructors and be able to use the new keyword.
With ES6 you can wrap a class inside a function, this is not yet supported by TypeScript (see https://github.com/Microsoft/TypeScript/issues/307).
Here what I do (MyFactory is now class TodoItem from a todo app to be more relevant):
class TodoItem {
title: string;
completed: boolean;
date: Date;
constructor(private injectable: SomeService) { }
doSomething() {
alert(this.injectable);
}
}
class TodoItemFactory() {
constructor(private injectable: SomeService) { }
create(): TodoItem {
return new TodoItem(this.injectable);
}
// JSON from the server
createFromJson(data: any): TodoItem {
var todoItem = new TodoItem(this.injectable);
todoItem.title = data.title;
todoItem.completed = data.completed;
todoItem.date = data.date;
return todoItem;
}
}
// In ES5: myApp.factory('TodoItem', function(injectable) { ... });
myApp.service('TodoItemFactory', TodoItemFactory);
class TodosCtrl {
// In ES5: myApp.controller('TodosCtrl', function(TodoItem) { ... });
constructor(private todoItemFactory: TodoItemFactory) { }
doSomething() {
// In ES5: var todoItem1 = new TodoItem();
var todoItem1 = this.todoItemFactory.create();
// In ES5: var todoItem2 = TodoItem.createFromJson(...)
var todoItem2 = this.todoItemFactory.createFromJson(
{title: "Meet with Alex", completed: false}
);
}
}
This is less elegant than with ES5 and functions (and not using classes with TypeScript is a no go) :-/
What I would like to write instead:
#Factory
#InjectServices(injectable: SomeService, ...)
class TodoItem {
title: string;
completed: boolean;
date: Date;
// No DI pollution
constructor() { }
saySomething() {
alert(this.injectable);
}
static createFromJson(data: string): TodoItem {
...
}
}
#Controller
#InjectFactories(TodoItem: TodoItem, ...)
class TodosCtrl {
constructor() { }
doSomething() {
var todoItem1 = new TodoItem();
var todoItem2 = TodoItem.createFromJson({title: "Meet with Alex"});
}
}
Or with functions:
myApp.factory(injectable: SomeService) {
class TodoItem {
title: string;
completed: boolean;
date: Date;
// No constructor pollution
constructor() { }
saySomething() {
alert(injectable);
}
static createFromJson(data: string): TodoItem {
...
}
}
}
myApp.controller(TodoItem: TodoItem) {
class TodosCtrl {
constructor() { }
doSomething() {
var todoItem1 = new TodoItem();
var todoItem2 = TodoItem.createFromJson({title: "Meet with Alex"});
}
}
}
I could write something like this
This is what I do
Can I create a TypeScript class within a function
No it needs to be at the top level of the file or in a module. Just FYI if were able to create it inside a function the information would be locked inside that function and at least the type info would be useless.
What's the reason for instantiating multiple instances of MyFactory? Would you not want a single instance of your factory to be injected into your dependent code?
I think using the class declaration you provided will actually look like this once injected:
function(myFactory) {
myFactory.saySomething();
}
If you are really needing to pass a constructor function into your dependent code, then I think you will have to ditch TypeScript classes, since they can't be defined inside of a function which means you would have no way to create a closure on a variable injected into such function.
You do always have the option of just using a function in TypeScript instead of a class. Still get the strong typing benefits and can call 'new' on it since it is still a .js function at the end of the day. Here's a slightly more TypeScriptiffied version:
myApp.factory('MyFactory', (injectable: ng.SomeService) => {
return (param: string) => {
return {
saySomething: () {
alert("Param=" + param + " injectable=" +injectable);
}
};
};
});

How can I define an AngularJS factory using TypeScript class that has constructor parameters

I want to write a TypeScript class that gets a "prefix" parameter in the constructor, this class also needs access to a LogService inject.
Using plain JavaScript you should do it like this:
angular.module('myModule', []).factory('LogWithPrefixFactory', ['LogService', function(LogService) {
var LogWithPrefixFactory = function(prefix) {
this.prefix = prefix;
}
LogWithPrefixFactory.prototype.log = function(txt) {
// we have access to the injected LogService
LogService.log(this.prefix, txt);
}
return LogWithPrefixFactory;
}]);
So when you inject this factory to a controller, you can initiate it many times like this (No need to inject the LogService):
angular.module('myModule').controller('Ctrl', function(LogWithPrefixFactory) {
var foo = new LogWithPrefixFactory("My PREFIX");
var foo = new LogWithPrefixFactory("My OTHER PREFIX");
}
How would you define this Factory in a TypeScript class?
TypeScript classes can not be defined inside functions...
This class should have access to the LogService, but it can't get it in one of the injects.
The following is one way to achieve this:
class LogWithPrefixFactory {
static LogService;
constructor(prefix) {
this.prefix = prefix;
}
log = function(txt) {
// we have access to the injected LogService
LogService.log(this.prefix, txt);
}
}
angular.module('myModule', []).factory('LogWithPrefixFactory', ['LogService', function(LogService) {
LogWithPrefixFactory.LogService = LogService;
return LogWithPrefixFactory;
}]);
angular.module('myModule').controller('Ctrl', function(LogWithPrefixFactory) {
var foo = new LogWithPrefixFactory("My PREFIX");
var foo = new LogWithPrefixFactory("My OTHER PREFIX");
});
Rational: You effectively want a static property in a the LogWithPrefixFactory (using a closure in JS) , and you want it to come from Angular.
There are at least 2 options.
First option, have LogWithPrefixFactory provide a method getInstance that returns the prefixed logger.
module services {
class LogService {
$window: any;
constructor($window: any) {
this.$window = $window;
}
log(prefix: string, txt: string) {
this.$window.alert(prefix + ' :: ' + txt);
}
}
angular.module('services').service('LogService', ['$window', LogService]);
export interface ILog {
log: (txt) => void;
}
export class LogWithPrefixFactory {
logService: LogService;
constructor(logService: LogService) {
this.logService = logService;
}
getInstance(prefix: string): ILog {
return {
log: (txt: string) => this.logService.log(prefix, txt);
}
}
}
angular.module('services').service('LogWithPrefixFactory', ['LogService', services.LogWithPrefixFactory]);
}
Which can be used in the controller like:
this.log1 = logWithPrefixFactory.getInstance("prefix1");
this.log2 = logWithPrefixFactory.getInstance("prefix2");
Complete plunker here.
Second option (similar to another answer), give Angular another function to be used as a constructor, which handles manually the LogService constructor injection (personally, I don't like static).
angular.module('services').service('LogWithPrefixFactory', ['LogService', function(logService) {
return function LogWithPrefixFactory(prefix) {
return new LogWithPrefix(prefix, logService);
};
}]);
Which can be used in the controller like:
this.log1 = new LogWithPrefixFactory("prefix1");
this.log2 = new LogWithPrefixFactory("prefix2");
or even:
this.log1 = LogWithPrefixFactory("prefix1");
this.log2 = LogWithPrefixFactory("prefix2");
LogWithPrefixFactory is injected in the controller but it's not the TypeScript class constructor, it's the intermediate function which returns the actual instance of the class, after it has been "manually" injected with LogService.
Complete plunker here.
Note: These plunkers synchronously compile typescript on the browser. I have tested it only on Chrome. No guarantees that they'll work. Finally, I manually added a small part of angular.d.ts. Full file was very big and my proxy does not allow large POSTs.
I have achieved like below
module Dashboard {
export class LayoutServiceFactory {
static $inject = ["$q", "$http"];
private q: ng.IQService;
private http: ng.IHttpService;
constructor(private $q: ng.IQService, private $http: ng.IHttpService) {
this.q = $q;
this.http = $http;
}
getDataFromServer(serviceUrl) {
var deferred = this.q.defer();
this.http.get(serviceUrl, null)
.then(response => {
deferred.resolve((response) as any);
});
return deferred.promise;
}
static factory() {
var instance = ($q: ng.IQService, $http: ng.IHttpService) =>
new LayoutServiceFactory($q, $http);
return instance;
}
}
appModule.factory("LayoutService", LayoutServiceFactory.factory());
}
This worked for me.
namespace Services
{
export class MyService
{
constructor( protected $someService :any )
{
return this;
}
}
}
angular.module( 'myModule', [] ).factory( Services );
this is how i do it
namespace app {
let app =angular.module('foo',[]);
app.factory(factories);//for registering whatever is there in factories namespace
}
namespace app.factories {
export class fooFactory {
static $inject = ['fooHelperService']
constructor(fooHelperService: services.fooHelperService) {
return {
fooFunc: () => {
return 'hellow world'
}
}
}
}
}
You can create a type that allows you to define what the constructor of the factory looks like:
// Defining the factory
// THIS IS THE IMPORTANT PART!!
export type SelectorFactory = new (config: any) => Selector;
export class Selector {
constructor(protected config: any, protected $http: ng.IHttpService) {
// do some stuff
}
}
angular.module('app')
.factory('Selector', ($http: ng.IHttpService) => {
// This is what the factory looks like to the end user
return (config: any) => {
return new Selector(config, $http);
};
});
// Using the factory
export class SampleCtrl {
constructor(public SelectorFactory: SelectorFactory) {
let config = { op: 1 };
let selector: Selector = new SelectorFactory(config);
}
}

Resources