How do I create a custom object class that's available to my methods in AngularJS - angularjs

I'm a huge fan of angular but it's got some tricky concepts with extremely nuanced differences between them and this is one of them.
I just want to create an class that I can use to create custom objects in my Angular controllers and factories. It surely shouldn't be that hard but I can't figure out how to do it. I want to have a custom, ResultSet class which I can instantiate to create instances of ResultSet. However for the life of me I can't figure out the correct syntax of factory v. service to use.
This is all I want:
ResultSet = function(dataSet){
this.filter = function(){
# filters and returns dataSet
# ...
}
}
and then I want to be able instantiate an instance of ResultSet inside a controller etc:
MyApp.controller('pageCtrl', ['ResultSet', (ResultSet) ->
# ...
rs = ResultSet.new(dataToFilter)
How can I create a service that allows me to create instances of my custom object?
It seems more correct to use an Angular Service rather than a Factory since a service returns an instance of an object (which is exactly what I want). But I can't figure out how to do this...
How would I use a service to declare my custom ResultSet class and then how would I instantiate an instance from it?

Maybe you were looking for something like this:
.factory('User', function (Organisation) {
/**
* Constructor, with class name
*/
function User(firstName, lastName, role, organisation) {
// Public properties, assigned to the instance ('this')
this.firstName = firstName;
this.lastName = lastName;
this.role = role;
this.organisation = organisation;
}
/**
* Public method, assigned to prototype
*/
User.prototype.getFullName = function () {
return this.firstName + ' ' + this.lastName;
};
/**
* Private property
*/
var possibleRoles = ['admin', 'editor', 'guest'];
/**
* Private function
*/
function checkRole(role) {
return possibleRoles.indexOf(role) !== -1;
}
/**
* Static property
* Using copy to prevent modifications to private property
*/
User.possibleRoles = angular.copy(possibleRoles);
/**
* Static method, assigned to class
* Instance ('this') is not available in static context
*/
User.build = function (data) {
if (!checkRole(data.role)) {
return;
}
return new User(
data.first_name,
data.last_name,
data.role,
Organisation.build(data.organisation) // another model
);
};
/**
* Return the constructor function
*/
return User;
})
From this post by Gert Hengeveld.

myApp.factory('ResulSet', function() {
function ResultSetInstance(dataSet) {
this.filter = function(){
// ...
}
}
return {
createNew: function(dataSet) {
return new ResultSetInstance(dataSet);
}
};
});
and then
myApp.controller('pageCtrl', function(ResultSet) {
var someData = ...;
var rs = ResultSet.createNew(someData);
}
Edit (from the question asker)
On experimenting with this further I found that you didn't even need to have the createNew method.
myApp.factory('ResultSetClass', function() {
ResultSetClass = function(dataSet) {
this.filter = function(){
// ...
}
}
return ResultSetClass
});
works just fine and then you can call new ResultSetClass(args).
Note for those using Coffeescript
Coffeescript will return the last variable or method in your class instance so if you are using coffeescript (as a general rule), it's imperative to return this at the end of the class definition
myApp.factory 'ResultSetClass', () ->
ResultSetClass = (dataset) ->
this.filter = () ->
# do some stuff
return this
return ResultSetClass
If you don't return this explicitly then you'll find that when you call
myApp.factory 'ResultSetClass', () ->
ResultSetClass = (dataset) ->
this.filter = () ->
# do some stuff
then you'll simply be left with the last thing the coffeescript returns which is the filter method.

I recently has do do something like that because I wanted to implement a factory of class instance, and being able to configurate my instances and benefit from Angular Dependency injection. I ended up with something like that
// Implem
export class XAPIService {
private path: string;
/* this DO NOT use angular injection, this is done in the factory below */
constructor(
private seed: XAPISeed,
private $http: ng.IHttpService,
private slugService: SlugService
) {
const PATH_MAP: Map<Y, Z> = new Map([
['x', id => `/x/${id}`],
['y', id => `/y/${id}`],
]);
this.path = PATH_MAP.get(this.seed.type)(this.seed.id);
}
list() {
/* implem that use configured path */
return this.slugService
.from(this.path + `/x`)
.then(url => this.$http.get<IX>(url))
.then(response => response.data)
}
}
export type IXAPIFactory = (s: XAPISeed) => XAPIService;
export function XAPIFactory(
$http: ng.IHttpService,
myService: SlugService
) {
'ngInject';
return (seed: XAPISeed) =>
new XAPIService(seed, $http, myService);
}
// angular
angular.module('xxx', [])
.factory('xAPIFactory', XAPIFactory)
// usage in code
export class XsController implements ng.IComponentController {
/* #ngInject */
constructor(
private xAPIFactory: IXAPIFactory,
) {}
$onInit() {
this.xService = this.xAPIFactory({ id: 'aaabbbaaabbb', type: 'y' });
return this.xService.list()
.then(xs => {
this.xs = xs;
})
}
}

Related

ngShow expression is evaluated too early

I have a angular component and controller that look like this:
export class MyController{
static $inject = [MyService.serviceId];
public elements: Array<string>;
public errorReceived : boolean;
private elementsService: MyService;
constructor(private $elementsService: MyService) {
this.errorReceived = false;
this.elementsService= $elementsService;
}
public $onInit = () => {
this.elements = this.getElements();
console.log("tiles: " + this.elements);
}
private getElements(): Array<string> {
let result: Array<string> = [];
this.elementsService.getElements().then((response) => {
result = response.data;
console.log(result);
}).catch(() => {
this.errorReceived = true;
});
console.log(result);
return result;
}
}
export class MyComponent implements ng.IComponentOptions {
static componentId = 'myId';
controller = MyController;
controllerAs = 'vm';
templateUrl = $partial => $partial.getPath('site.html');
}
MyService implementation looks like this:
export class MyService {
static serviceId = 'myService';
private http: ng.IHttpService;
constructor(private $http: ng.IHttpService) {
this.http = $http;
}
public getElements(): ng.IPromise<{}> {
return this.http.get('./rest/elements');
}
}
The problem that I face is that the array elements contains an empty array after the call of onInit(). However, later, I see that data was received since the success function in getELements() is called and the elements are written to the console.
elements I used in my template to decide whether a specific element should be shown:
<div>
<elements ng-show="vm.elements.indexOf('A') != -1"></elements>
</div>
The problem now is that vm.elements first contains an empty array, and only later, the array is filled with the actual value. But then this expression in the template has already been evaluated. How can I change that?
Your current implementation doesn't make sense. You need to understand how promises and asynchronous constructs work in this language in order to achieve your goal. Fortunately this isn't too hard.
The problem with your current implementation is that your init method immediately returns an empty array. It doesn't return the result of the service call so the property in your controller is simply bound again to an empty array which is not what you want.
Consider the following instead:
export class MyController {
elements: string[] = [];
$onInit = () => {
this.getElements()
.then(elements => {
this.elements = elements;
});
};
getElements() {
return this.elementsService
.getElements()
.then(response => response.data)
.catch(() => {
this.errorReceived = true;
});
}
}
You can make this more readable by leveraging async/await
export class MyController {
elements: string[] = [];
$onInit = async () => {
this.elements = await this.getElements();
};
async getElements() {
try {
const {data} = await this.elementsService.getElements();
return data;
}
catch {
this.errorReceived = true;
}
}
}
Notice how the above enables the use of standard try/catch syntax. This is one of the many advantages of async/await.
One more thing worth noting is that your data services should unwrap the response, the data property, and return that so that your controller is not concerned with the semantics of the HTTP service.

What happens when I use angular.module(...).controller(...).directive(...)?And why? [duplicate]

This question already has answers here:
How to implement chained method calls like jQuery?
(4 answers)
Closed 5 years ago.
I think this equals to
var module = angular.module(...);
module.controller(...);
module.directive(...);
But I'm not sure. And I don't know what happens in angular and why I could write code this way.
I try to debug and trace it but it's so confused.
This is called a fluent API.
Each method will return the module instance, so that another method may be called.
To illustrate we can create a class that does something similar.
class Module {
controller() {
console.log('controller');
return this;
}
directive() {
console.log('directive');
return this;
}
}
When each method has finished, it will return the module instance this so that another method can be chained.
So now we can use this class and chain the methods like this:
new Module().controller().directive();
Or
const module = new Module();
module.controller();
module.directive();
What happens when I use angular.module(…).controller(…).directive(…)?And why?
Short answer
This is good way to write your code in one file.
If you want to split Angular project to different files, use 2nd approach:
var app = angular.module(...);
app.controller(...);
app.directive(...);
Long answer
Also take a look on this angular code snippets (took from https://code.angularjs.org/1.5.6/angular.js):
You can see controller, directive, module, filter, factory, value, provider, decorator, animation, config, component ,run returns moduleInstance
function setupModuleLoader(window) {
var $injectorMinErr = minErr('$injector');
var ngMinErr = minErr('ng');
function ensure(obj, name, factory) {
return obj[name] || (obj[name] = factory());
}
var angular = ensure(window, 'angular', Object);
// We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
angular.$$minErr = angular.$$minErr || minErr;
return ensure(angular, 'module', function() {
/** #type {Object.<string, angular.Module>} */
var modules = {};
return function module(name, requires, configFn) {
var assertNotHasOwnProperty = function(name, context) {
if (name === 'hasOwnProperty') {
throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
}
};
assertNotHasOwnProperty(name, 'module');
if (requires && modules.hasOwnProperty(name)) {
modules[name] = null;
}
return ensure(modules, name, function() {
if (!requires) {
throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
"the module name or forgot to load it. If registering a module ensure that you " +
"specify the dependencies as the second argument.", name);
}
/** #type {!Array.<Array.<*>>} */
var invokeQueue = [];
/** #type {!Array.<Function>} */
var configBlocks = [];
/** #type {!Array.<Function>} */
var runBlocks = [];
var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
/** #type {angular.Module} */
var moduleInstance = {
// Private state
_invokeQueue: invokeQueue,
_configBlocks: configBlocks,
_runBlocks: runBlocks,
requires: requires,
name: name,
provider: invokeLaterAndSetModuleName('$provide', 'provider'),
factory: invokeLaterAndSetModuleName('$provide', 'factory'),
service: invokeLaterAndSetModuleName('$provide', 'service'),
value: invokeLater('$provide', 'value'),
constant: invokeLater('$provide', 'constant', 'unshift'),
decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
component: invokeLaterAndSetModuleName('$compileProvider', 'component'),
config: config,
run: function(block) {
runBlocks.push(block);
return this;
}
};
if (configFn) {
config(configFn);
}
return moduleInstance;
function invokeLater(provider, method, insertMethod, queue) {
if (!queue) queue = invokeQueue;
return function() {
queue[insertMethod || 'push']([provider, method, arguments]);
return moduleInstance;
};
}
function invokeLaterAndSetModuleName(provider, method) {
return function(recipeName, factoryFunction) {
if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
invokeQueue.push([provider, method, arguments]);
return moduleInstance;
};
}
});
};
});
}
Why is it better?
Both approaches do the same so developer will decide what is better for his project structure
for efficiency?
There is no efficiency value measurement, both has same efficiency. No performance penalty.
for what?
In project I want to write each directive each controller each ... in separate file so I use app.controller(...);, app.service(...); ,...
However common directives I want to put in one file so I use:
app.directive(…).directive(…).directive(…).directive(…).directive(…)
Hope it will spread the light on your understanding :)

Angular - Organise controller, factory and "class"

I would like to understand how to have a nice organisation in my angular project.
[see code below]
Does it makes sense to have the getFireList function into the Factory ? Or should i put it into the controller ?
Does the "class" Fire makes sense ? Should i remove it ? Should i move it to the controller ? Should i move it the the factory ?
If you see anything wrong in this code i'm really interested to learn more.
For now, i've got this :
A class "Fire" to create new object of type Fire.
function Fire (p_power) {
// ATTRIBUTES
this.id = null;
this.power = p_power;
this.position = {
x: null,
y: null
}
// GETTERS/SETTERS
// id
this.getId = function() {
return this.id;
}
this.setId = function(p_id) {
this.id = p_id;
}
// power
this.getPower = function() {
return this.power;
}
this.setPower = function(p_power) {
this.power = p_power;
}
// position
this.getPosition = function() {
return this.position;
}
this.setPosition = function(p_position) {
this.position = p_position;
}
// METHODS
this.increasePower = function(p_plus) {
this.power += p_plus;
}
this.decreasePower = function(p_minus) {
this.power -= p_minus;
}
}
A controller
simuApp.controller('FireController', function($scope, FireFactory) {
// ...
});
And a factory
simuApp.factory('FireFactory', function() {
return {
fire_list: [],
getFireList : function() {
return $http.get(site_url+'fire/fireList').
then(
function(success) {
var data = success.data;
var fires = [];
var fire_tmp;
for (i=0 ; i<data.length ; i++) {
fire_tmp = new Fire( data[i].power );
fire_tmp.setId( data[i].idFire );
fires.push( fire_tmp );
}
fire_list = fires;
return fire_list;
}, function(err) {
// ...
}
);
}
}
});
Thanks for your help.
First, let's get the terminology right. .factory is a method to register a function that generates an instance of the service - hence "factory". What it generates, though, is a singleton service instance.
So, the service you create would be more properly named as FireSvc (as opposed to FireFactory), whereas the function that creates it could have the word "factory" in it (although, in the case below, that function name is not really needed - it could just be an anonymous function):
.factory("FireSvc", function FireSvcFactory(){
});
It is a good practice to use a Service to abstract away any domain/business logic from the controller. Keep the controller thin, responsible only to define the ViewModel, and react to events by changing the ViewModel or invoking functions on the Model.
So, having FireSvc.getFireList() makes sense.
Now, whether the list is a collection of plain objects, or instances of Fire is completely independent of Angular and is entirely up to you. In any case, it is too broad of a topic to discuss in a SO answer.

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