Integrating helper functions in AngularJS - angularjs

So, I've just began learning Angular and my question is what ways (or the best practices) are there for injecting helper functions into AngularJS? I often need to include functions to assist my controller, but I have read online that the controller should hold as little logic as possible, which means they should be injected into the controller and declared in the module (fat module, skinny controller).
As such, I have been primarily injecting functions like this:
$provide.value
$provide.value('MySQLtoJS', function(datetimeString) {
var t = datetimeString.split(/[- :]/);
var d = new Date(t[0], t[1]-1, t[2], t[3], t[4], t[5]);
return d;
});
This uses the $provide service to create a value that can be injected in my controller. However, for more elaborate functions, such as those that require an injectable, I have been using this:
Factory provider
.factory('convertMySQLToJS', ['moment', function(moment) {
return function(arrayInput) {
if (Array.isArray(arrayInput)) {
for (var i = 0; i < arrayInput.length; i++) {
var t = arrayInput[i].begin_datetime.split(/[- :]/);
var start = new Date(t[0], t[1]-1, t[2], t[3], t[4], t[5]);
arrayInput[i].begin_datetime = start;
var t = arrayInput[i].end_datetime.split(/[- :]/);
var end = new Date(t[0], t[1]-1, t[2], t[3], t[4], t[5]);
arrayInput[i].end_datetime = end;
//Also create the moment message
if (arrayInput[i].begin_datetime >= new Date()) {
arrayInput[i].message = 'Begins at ' + moment(arrayInput[i].begin_datetime).format('MMMM Do YYYY, h:mm a') + ' and likely ends at ' + moment(arrayInput[i].end_datetime).format('MMMM Do YYYY, h:mm a');
}
else {
arrayInput[i].message = 'Began at ' + moment(arrayInput[i].begin_datetime).format('MMMM Do YYYY, h:mm a') + ' and likely ended at ' + moment(arrayInput[i].end_datetime).format('MMMM Do YYYY, h:mm a');
}
}
return arrayInput;
}
}
}])
However, factories are often used for their service and properties (like $http), or so I've been told. So I've been recently suggested by some people that I should be including them in the run configuration block (which seems a bit weird to me) by using $rootScope and giving it that property for the function I need. Since I'm new to Angular and I've found documentation rather lacking, I'm wondering how am I supposed to inject helper functions correctly into Angular, if there is a correct way?

The correct way in Angular is to wrap helpers into services and inject them when needed. It is a good idea to join several similarly themed methods into single helper service (think of it as of utility class).
It can be either factory, or value, or constant. The latter is preferable for such things because it can be also used within config blocks. They are interchangeable in other respects, as long as the factory function consists of return statement and doesn't use other dependencies. Since this one
app.factory('mysqlHelper', function (moment) {
return {
MySQLtoJS: function(datetimeString) { ... },
convertMySQLToJS: return function(arrayInput) { ... }
};
});
uses moment dependency, factory is the case for it.
Using globals (either on global JS scope or $rootScope) is considered bad practice:
Of course, global state sucks and you should use $rootScope sparingly,
like you would (hopefully) use with global variables in any language.
In particular, don't use it for code, only data. If you're tempted to
put a function on $rootScope, it's almost always better to put it in a
service that can be injected where it's needed, and more easily
tested.
And thus it provides a reasoning for that: testability. Services are testable. They can be unit-tested, they can be mocked. That is where Angular dependency injection shines.

Factories/services are certainly a way to do something like this. If it is always related to service activities such as sanitizing your data after retrieval, placing it in a service (or base service) works just fine. However, I find that always injecting services is a bit heavy when I have one-off helper functions that I need to pass around my app. I've ended up placing a special object on angular that holds my helper functions.
app.run([function() {
angular.UTIL = angular.UTIL || {};
var util = {
coolFunction: function(fieldName) {
return fieldName;
}
}
angular.extend(angular.UTIL, util);
}]);
This can then be called throughout your app:
var getField = angular.UTIL.coolFunction("fieldName");
As far as best practice goes with helpers such as these, it is best if you follow a few rules:
The helper functions are global and can easily be used throughout the codebase in a variety of applications and patterns
They cannot be easily translated into a directive and do not directly manipulate the DOM
They are helpers, not functionality or business logic
They replace utility functions that have been copied into multiple files
They are not bound to a specific scope

Related

adding new property to an object exposed to scope in angularjs

I have an object that is exposed to $scope and has properties attached to it.
I access this object in my controller and call a service which does little something.
What I need to add to it is new property to the object(which is going to be an array attached to the object as a property) so that updated object is returned to the controller and I can access the elements of this array using data-expression tag{{ }}.
I would like to know in detail about making such manipulations the right way and possible ways of doing it.
Just add the array to the object.
$scope.myobj = {};
...
// sometime later
var numbers = [1,2,3];
$scope.myobj.numbers = numbers;
EDIT:
To answer your question about scope in a service. Scope is not accessible in a service. Typically you ask your service for something IE data. But your service can do anything, like add 2 numbers, or in your case create an array of something that you need to attach to your object.
module.service('MyService', function() {
this.add = function(number1, number2) {
return number1 + number2;
}
this.createMyArray = function() {
// pass in whatever you need to this service in order
// to create the array you need.
// example of just returning a hard coded array
return [1,2,3];
}
});
Then you can inject your service into your controller which has the scope you want to modify.
app.controller('MyController', function($scope, MyService) {
$scope.add = function(number1, number2) {
// Lets use our service to add these numbers and
// assign result to scope variable
$scope.answer = MyService.add(number1, number2);
}
$scope.myobj = {};
$scope.makeArray = function() {
// lets use our service again to create an array
// this time lets add it to a scope object that already exists.
$scope.myobj.numbers = MyService.createMyArray();
}
});
A lot of times services are used to grab/update things from a server, so you will see a lot of examples that make http get/post/put/delete calls. Then from your controller(s) you can use those services to grab data. But again you are not limited to that, your service can simple just hold some static data or helper functions too.

How to prevent a directive from binding to elements within a controllers scope in Angular?

I have an Angular app, MyApp, that depends on external modules (two different map solutions), and I need them both but in different controllers (different modules within MyApp even).
The problem is the two modules both have directives that bind to the same argument ('center' in this case), which causes them both do manipulate a single element. What I want is for one directive to be active inside one controller and the other directive to be active inside another controller - so not have them inpact my elements at the same time.
I don't want to change the code of the external modules to achive this.
I found this to be a very interesting question. The answer below is incomplete, and, frankly, a bit hackish, but it demonstrates a way to rename a directive in another module without modifying the source of the module itself. There is a lot of work to do to make this anywhere near production ready and it absolutely can be improved.
The caveats to the solution are that once a directive is renamed, the "old" name will no longer work. It also depends on some angular conventions that might be changed with future versions, etc, so it's not future proof. It also might fail for complex directives, and I haven't really done any testing on it.
However, it demonstrates that it can be done, and the concept might lead to a feature angular needs (the ability to namespace external modules in order to prevent conflicts such as the one your are experiencing).
I think that if your use case is fairly simple, this will solve your problem, but I wouldn't recommend using it in the general case yet.
(function () {
var util = angular.module('util', [], function ($compileProvider) {
util.$compileProvider = $compileProvider
})
.factory('$directiveRename', function () {
var noop = function () { return function () { }; };
return function (module, directive, newDirective) {
var injector = angular.injector(['ng', module]);
var directive = injector.get(directive + 'Directive');
if(directive)
{
//there can be multiple directives with the same name but different priority, etc. This is an area where this could definitely be improved. Will only work with simple directives.
var renamedDirective = angular.copy(directive[0]);
delete renamedDirective['name'];
util.$compileProvider.directive(newDirective, function () {
return renamedDirective;
});
}
//noop the old directive
//http: //stackoverflow.com/questions/16734506/remove-a-directive-from-module-in-angular
angular.module(module).factory(directive + 'Directive', noop);
};
});
})();
Example usage:
angular.module('app', ['module1', 'module2', 'util'])
.run(function ($directiveRename) {
$directiveRename('module1', 'test', 'testa');
$directiveRename('module2', 'test', 'testb');
});
An alternative, slightly less hackish answer.
Add the following immediately after the script tag that includes angular (before any other modules are loaded)
<script type="text/javascript">
var angularModule = angular.bind(angular, angular.module);
angular.module = function (moduleName, requires, configFn) {
var instance = angularModule(moduleName, requires, configFn);
var directive = instance.directive;
instance.directive = function (directiveName, directiveFactory) {
//You can rename your directive here based on the module and directive name. I don't know the module and directive names for your particular problem. This obviously could be generalized.
if (instance.name == 'module1' && directiveName == 'test') {
directiveName = 'testa';
}
if (instance.name == 'module2' && directiveName == 'test') {
directiveName = 'testb';
}
return directive(directiveName, directiveFactory);
}
return instance;
};
</script>
This works by intercepting calls to module.directive and allowing you the opportunity to rename the directive before it is created.

Factory without inject the dependency

This is my code that currently works:
angular.module('myApp')
.controller('myCtrl', function (DocumentTypeManagerPdf, DocumentTypeManagerVideo) {
$scope.showPreview = function(document){
var previewModule = eval('DocumentTypeManager' + document.clientModule);
previewModule.show(document);
};
});
but... two things I would avoid:
Eval is evil
I am forced to inject every DocumentTypeManagerXYZ that I'll implement
In there a better solution tu use a Factory dynamically?
I think you should go with a factory pattern.
One service DocumentTypeManagerFactory
With one method like
var myDocumentTypeManager = DocumentTypeManagerFactory.instanciateWithType(document.clientModule);
myDocumentTypeManager.show(document);
Your controller will only inject one service (and the DocumentTypeManagerFactory should inject all)
In your DocumentTypeManagerFactory you should make a switch or if/else to avoid eval.
I think you can use arguments in the function. inJS every function has a variable named arguments which is a array of given parameters.
But I am not sure how your DocumentTypeManagerXYZ objects are structured. So just type debugger; beginning of your controller function and check arguments data by console then you can take a correct action.
the below one is the first one comes to my mind;
var previewModule;
for(var i = 0, len=arguments.lengh; i <len; i++) {
if (arguments[i].constructure.name === 'DocumentTypeManager' + document.clientModule) {
previewModule = arguments[i];
break;
}
}
this will be your basic approach.
as this is an angular application you can user $injector.get("moduleName")
for example;
var previewModule = $injector.get("'DocumentTypeManager' + document.clientModule");
please see $injector

Is it good practice to combine CREATE and EDIT controllers in AngularJS?

There are many duplicated code among CREATE and EDIT controllers.
These controllers could be combined into one for minimizing repetitive code.
The problem: I need to distinguish which method to use on form submitting - create() or edit() for example.
The solution: I could add $scope.mode for example and set $scope.mode='edit' if user clicked 'EDIT' button or set $scope.mode='add' if user clicked 'ADD' button.
I could use services for minimizing repetitive code, but there still will be duplicated code. For example in both controllers I have cancel() method which clears the form and hide it. I could store clearForm() and hideForm() in the service, but this code will be duplicated in both controllers:
$scope.cancel = function() {
Service.clearForm();
Service.hideForm();
};
Questions:
Is it good practice to combine CREATE and EDIT controllers in AngularJS?
Is there any good practices to minimize repetitive code?
Yes. Use 1 controller.
Here is the reason why use 1 controller
The job of the controller is to support the View. Your create view and the edit view is exactly same - just that one has data pre-populated (edit) and another does not (create).
Moreover the "purpose" of this View is to have the user change or enter new values in the form. Your only difference should be something like reset(). But even there you could start with an empty model object e.g. $scope.entity = {} in case of CREATE and you will start with $scope.entity = $http.get().
Repetition Problem with 2 Controllers
With 2 different controllers and services you are going to incur at least the following duplication:
$scope.cancel = function() {
Service.cancel();
};
$scope.validate = function() {
ValidtionSvc.validate();
}
.
.
.//other stuff similar
but the problem is why even this duplication like you stated.
(UDATED here onwards since above was the answer to the 1st question)
How to use 1 controller with repetition ?
Is there any good practices to minimize repetitive code?
Question redefined: Is there a good practice of eliminating repetitive code in CREATE and EDIT forms ?
No formal 'best practice' exist to my knowledge to avoid repetitive code in this specific situation. However I am advising against mode=edit/create. The reason being for controllers in this situation there should be almost no difference since their job is to purely to fetch/update the model as the user interacts.
Here are the difference you will encounter in this situation and how you can avoid if/then/else with mode=create/edit:
1) Populating the form with existing values vs. empty form for Create.
To fetch a existing entities you need some key/query data. If such key data is present you could do
var masterEntity = {};
if(keyData) {
masterEntity = MyEntityResourceFactory.getEntity(keyData);
}
$scope.entity = masterEntity;//for Create this would be {}
2) reset() form
should be simply
$scope.reset = function() {
$scope.entity = masterEntity;
}
3) Update/Create
$http.post()//should not be different in today's world since we are treating PUT as POST
4) Validation - this is a perfect reuse - there should be no differences.
5) Initial / Default Values
You can use masterEntity = Defaults instead of {}.
Is it good practice to combine CREATE and EDIT controllers in
AngularJS?
In my experience, yes it is a good idea for 99.9% of the time. I typically inject a formType variable into my controller via the $routeProvider resolve feature. So I would have something like the following:
$routeProvider
.when('/item/create', {
templateUrl: '/app/item/itemForm.html',
controller: 'itemFormController',
resolve: {
item: ['$route', 'itemRepository', function ($route, itemRepository) {
return itemRepository.getNew();
}],
formType: function () { return Enums.FormType.CREATE; }
},
})
.when('/item/edit/:itemId', {
templateUrl: '/app/item/itemForm.html',
controller: 'itemFormController',
resolve: {
item: ['$route', 'itemRepository', function ($route, itemRepository) {
return itemRepository.get($route.current.params.itemId);
}],
formType: function () { return Enums.FormType.EDIT; },
},
});
That way you get your entity and type of form action injected into the controller. I also share the same templates, so saving a form I can either rely on my repository/service to determine what REST endpoint to call, or I can do a simple check inside the controller depending on what formType was injected.
Is there any good practices to minimize repetitive code?
Some of the things I'm using to keep things DRY:
If you keep a common convention on your server API you can go a very long way with a base factory/repository/class (whatever you want to call it) for data access. For instance:
GET -> /{resource}?listQueryString // Return resource list
GET -> /{resource}/{id} // Return single resource
GET -> /{resource}/{id}/{resource}view // Return display representation of resource
PUT -> /{resource}/{id} // Update existing resource
POST -> /{resource}/ // Create new resource
etc.
We then use a AngularJs factory that returns a base repository class, lets call it abstractRepository. Then for each resource I create a concrete repository for that specific resource that prototypically inherits from abstractRepository, so I inherit all the shared/base features from abstractRepository and define any resource specific features to the concrete repository. This way the vast majority of data access code can be defined in the abstractRepository. Here's an example using Restangular:
abstractRepository
app.factory('abstractRepository', [function () {
function abstractRepository(restangular, route) {
this.restangular = restangular;
this.route = route;
}
abstractRepository.prototype = {
getList: function (params) {
return this.restangular.all(this.route).getList(params);
},
get: function (id) {
return this.restangular.one(this.route, id).get();
},
getView: function (id) {
return this.restangular.one(this.route, id).one(this.route + 'view').get();
},
update: function (updatedResource) {
return updatedResource.put();
},
create: function (newResource) {
return this.restangular.all(this.route).post(newResource);
}
// etc.
};
abstractRepository.extend = function (repository) {
repository.prototype = Object.create(abstractRepository.prototype);
repository.prototype.constructor = repository;
};
return abstractRepository;
}]);
Concrete repository, let's use customer as an example:
app.factory('customerRepository', ['Restangular', 'abstractRepository', function (restangular, abstractRepository) {
function customerRepository() {
abstractRepository.call(this, restangular, 'customers');
}
abstractRepository.extend(customerRepository);
return new customerRepository();
}]);
What you'll find if you use this base repository pattern is that most of your CRUD controllers will also share a lot of common code, so I typically create a base CRUD controller that my controllers inherit from. Some people dont like the idea of a base controller, but in our case it has served as well.
The answer to your first question probably depends on the specific circumstances.
If the two controllers share a substantial amount of operations, and the behavior of just one or two functions needs to be altered - why not! Maybe not the most elegant solution but hey, whatever works.
If the behavior of many or all controller operations is going to depend on '$scope.mode'...I'd say be careful. That seems like a dangerous path.
Angular services have always served me well when it comes to minimizing code replication between controllers. If there is a "good practice to minimizing repetitive code," I would say it would be services. They are global to your app and can be injected into multiple controllers without issue.
I hope that helps!

How to pass a lambda expression to an AngularJS directive

I'm trying to create a set of AngularJS directives that will process an array of objects and perform specific operations using either the objects themselves or perhaps a property or sub-property of the each instance.
For example, if the array contains strings, one such directive might render a comma-separated list of those strings. I anticipate using such a directive like this:
<csv-list items="myArray" />
However, as stated above, I want the implementation to be flexible enough to pass an array of objects to the directive, whereby the directive can be instructed to act on a specific property or sub-property of each instance. If I could pass a lambda expression to the directive, I would imagine using it something like this:
<csv-list items="myArray" member="element => element.name" />
I guess there's a recommended AngularJS pattern to solve such problems, but I am quite new to AngularJS, so I haven't found it yet. Any suggestions would be appreciated.
Thanks,
Tim
There are several ways to do this, Using the $parse service may be the easiest
var parser = $parse("name");
var element = {name:"thingA"};
var x = parser(element);
console.log(x); // "thingA"
Parse has been optimized to act quickly in these scenarios (single property look-ups). You can keep the same "parser" function around and invoke it on each element.
You could also split on the '.' and do the simple look-up yourself (reading in 'member' to your directive as a string), in simple form:
var paths = myPath.split('.');
var val = myObj;
for(var i = 0; i < paths.length; i++){
val = val[paths[i]];
}
return val;
There are also various linq-like libraries that support lambda expressions as strings (linqjs, fromjs). If you've gotta have a fat arrow function.
Your directive can look at other attributes, so you could add a property-name attribute and have your directive manually check that property. To be fancy you could use $parse like ng-repeat does to parse an expression.
<csv-list items="element in myArray" member="element.name">
Another way would be to create a 'property' filter that takes an array of objects and returns an array of property values from that object that you could use like so:
<csv-list items="myArray|property:name">
Here's what you're asking for syntactically (Show me the code - plunkr):
member="'e' |lambda: 'e.name'"
You can do this with something like (I wrote this just for the question, what I do in my apps is outlined below)
app.filter('lambda', [
'$parse',
function ($parse) {
return function (lambdaArgs, lambdaExpression, scope) {
var parsed = $parse(lambdaExpression);
var split = lambdaArgs.split(',');
var result = function () {
var args = {};
angular.extend(args, scope || {});
for (var i = 0; i < arguments.length && i < split.length; i++) {
args[split[i]] = arguments[i];
}
return parsed(args);
};
return result;
}
}
]);
Advanced usage:
(x, y, z) => x * y * z + a // a is defined on scope
'x,y,z' |lambda: 'x * y * z + a':this
The :this will pass the scope along to the lambda so it can see variables there, too. You could also pass in an aliased controller if you prefer. Note that you can also stick filters inside the first argument to the lambda filter, like:
('x'|lambda:'x | currency')(123.45) // $123.45 assuming en-US locale
HOWEVER I have thus far avoided a lambda filter in my apps by the following:
The first approach I've taken to deal with that is to use lodash-like filters.
So if I have an array of objects and your case and I want to do names, I might do:
myArray | pluck:'name'
Where pluck is a filter that looks like:
angular.module('...', [
]).filter('pluck', [
function () {
return function (collection, property) {
if (collection === undefined) {
return;
}
try {
return _.pluck(collection, property);
} catch (e) {
}
}
}
]);
I've implemented contains, every, first, keys, last, pluck, range (used like [] | range:6 for [0,1,2,3,4,5]), some, and values. You can do a lot with just those by chaining them. In all instances. I literally just wrapped the lodash library.
The second approach I've taken is to define functions inside a controller, expose them on the scope.
So in your example I'd have my controller do something like:
$scope.selectName = function (item) { return item.name };
And then have the directive accept an expression - & - and pass selectName to the expression and call the expression as a function in the directive. This is probably what the Angular team would recommend, since in-lining in the view is not easily unit-test-able (which is probably why they didn't implement lambdas). (I don't really like this, though, as sometimes (like in your case) it's strictly a presentation-thing - not a functionality-thing and should be tested in an E2E/Boundary test, not a unit test. I disagree that every little thing should be unit tested as that often times results in architecture that is (overly) complicated (imho), and E2E tests will catch the same thing. So I do not recommend this route, personally, though again I think the team would.)
3.
The third approach I've taken would be to have the directive in question accept a property-name as a string. I have an orderableList directive that does just that.

Resources