Managing Angular Module names in a large project - angularjs

I'm working on a a large scale angular project with a team of devs.
the problem we run into is if you have several files for a component, say a directive.
some-directive.js
some-directive-controller.js
in the definition of both files you would have to attach them to a module, however one file must create the module with []. If the developer forgets to poke around they will add [] in the second file called will actually overwrite the module. so now it becomes a memory game. Each developer has to remember to only declare the module in one file []
some-directive.js
angular.module('some-module',['some-dependencies']).directive('some-directive',function(){});
some-controller.js
angular.module('some-module',[]).controller('some-controller',function(){});
we have been using the following approach. Is there a better way?
some-directive.js
some-directive-module.js
some-directive-controller.js
where some-directive-module only contains the module creation, includes any dependencies, and does any .config needed. Still the dev needs to remember to
angular.module('some-directive') in all the other files without the square brackets.
some-directive-module.js
angular.module('some-directive',[])
.config(//someconfig stuff);
some-directive-module.js
angular.module('some-directive).directive(//declare directive);
some-directive-controller.js
angular.module('some-directive).controller(//declare contrller used by directive);
I suggested that instead we should do the following, it eliminates the issue of overwriting modules, but I received some negative feedback from one of the other devs
some-directive-module.js
angular.module('some-directive',['some-directive.directive','some-directive.controller'])
.config(//someconfig stuff);
some-directive-module.js
angular.module('some-directive.directive',[]).directive(//declare directive);
some-directive-controller.js
angular.module('some-directive.controller',[]).controller(//declare contrller used by directive);
Is there a better way? Or is one of the above options correct?

The recommended way (by multiple competent people) is to use the setter-getter-syntax (creating once with angular.module("someModule",[]) and accessing with angular.module("someModule") from there on). Putting the module definition and configuration into one file seems very clean and is common practice between a lot of developers. But make sure not to create a module for every single directive - group services, directives, constants and so on into reasonable functionality-modules instead.
Making clear what a file contains by its name is also a good idea in my opinion, so your some-directive-module.js approach seems fine to me. If developers "poke around" and "wildly add []", they should get a slap on the wrist follwoed by an explanation how modules work in angular, so they stop doing it ;-)

Related

Why are global functions considered "wrong" in Angular 1.3

Traditionally I have managed my Angular code like this
//File 1
angular.module('name',[])
//File 2
function TestController(){
}
TestController.prototype.// inherited stuff
angular.module('name').controller('testController',TestController);
This worked great and allowed me to partition my files easily. Now I try to upgrade to 1.3 and get the infamous...
Error: [ng:areq] Argument 'TestController' is not a function, got undefined
Of course this is due to this change which claims a desire to clean up the way people write code. What about this pattern is more complex? Is there a way to maintain this pattern without changing the global settings?
There is actually a comment on the page you linked to that had a fairly solid explanation.
Global controllers refer to your controllers being defined as function
on the window object. This means that they are openly available to
conflict with any other bit of JavaScript that happens to define a
function with the same name. Admittedly, if you post-fix your
controllers with ...Controller then this could well not happen but
there is always the chance, especially if you were to use a number of
3rd party libraries. It is much safer to put these controller
functions inside the safety of a module. You then have more control
over when and where this module gets loaded. Unfortunately controller
names are global across an individual Angular app and so you still
have the potential for conflict but at least you can't clash with
completely different code in the JavaScript global namespace.
So the idea is that global controller functions could conflict with any other global function in any javascript you use. So to eliminate the chance of a conflict with your own code or a third-party script, not using global controllers makes your code safer and more consistent.
As mentioned in the comments by #Brett, you can use IIFE around your prototyping. Here is an update of your plunk that uses that. The main change just looks like this.
(function() {
TestController.prototype.name = 'World'
})();
What comes to my mind is 2 things:
1) in that way functions wont be kept in memory more than they should.
2) if you minify your code, minifyer will have to generate new names for all global objects, which is sfine when you have small project, but will be a problem when it's not.
Also it should prevent tests to modify unnecessary data.

In an AngularJS expression is there a way to compare a scope value to a value in another library?

I'm creating a directive around a third party library, to go in a form, where the option chosen in a select drop-down will bring up a different set of form elements.
In the parent element of each subset of form elements I'm trying to use an expression similar to this: ng-if="myScopeObj.val === ThirdParty.CONSTANT_VAL". I came to realize it's not working because the "ThirdParty" library isn't on the scope.
Should I just assign the library to a variable on the scope, or is there some pattern that can address this? It seems like creating isThis() or isThat() functions for every constant in the library wouldn't be a great solution.
Should I create a service to wrap the third party library and then inject it into the directive? Though I'd still need to put the injected service on the scope. Would that be overkill for a library that doesn't access remote APIs? I don't think it'd need to be mocked for testing, anyway.
You're correct that you do need to get the value on the $scope somehow in order for it to be usable. And you're correct that one of the primary benefits of wrapping in a service is that you can mock the library. Another benefit of wrapping in a service is self-documentation. As someone else (or yourself at a later time) looking at your code, I could be confused as to where ThirdParty is coming from. Working in Angular, the assumption is that all dependencies are injected, and breaking convention comes at a cognitive cost. Having a service also can make it easier to swap out the underlying library later for a different implementation. Anyway, your simplest fix is:
$scope.ThirdParty = ThirdParty;

Global variables across modules

OK, so this is the concept :
I'm currently writing a fairly complex project, consisting of 10's of different modules and classes.
I need to have one basic set of variables/options (an associative array?) which will be shared (read/write) by all modules (or selected ones) at any time.
What would be the most D-friendly way to achieve this?
UPDATE:
Hmm... just created a variable definition in one module (let's say globals.d module) and no matter where I import it, I can always get/set it. That simple?! (Or am I missing anything?)
Just filling this in so there's an answer: yes, you generally would want to make a new module like globals.d and simply import it from all the other modules that use it.

AngularJS - when defining a module, what is the meaning of third argument when it is an array?

I am new to AngularJS. I have been reading the excellent book Mastering Web Application Development with AngularJS by Pawel Kozlowski and Peter Bacon Darwin. However, I am still a bit fuzzy on some concepts, so I have decided to go through their sample application line by line to try and get a better understanding of how AngularJS is used in a real-world app.
In some places I see notation that I can't see explained in their book, nor in the API docs. I am wondering if anyone can shed some light on this, as seen in the /client/src/app/projectsinfo/projectsinfo.js file of the project linked to above:
angular.module('projectsinfo', [], ['$routeProvider', function($routeProvider){
...
}]);
My understanding of the angular.module method is that it accepts three arguments:
the name of the module
an array of other modules that this module might depend on
an optional configuration function
However, in the above example, for the third argument an array is being provided, with the first element in the array being a string (I am assuming a provider?), followed by a function. Can anyone explain what is going on here?
The syntax of angular.module() is:
angular.module(name, [requires], configFn);
where:
name is the name of the module,
requires is an optional list of modules on which this module depends, and
configFn is a function used to configure the module.
Here the configFn can be a function or a array:
If it is a function, then the dependencies injected to it will be injected based on the name of the parameters.
If it is an array, then we can use the array to specify the name of the services that need to be injected and then use some other name as the function parameter. This is useful when your code might be obfuscated by a minifier.
The code in the said files seems to be fine.

Loading CakePHP Helpers

This is a multi part question.
Background:
I'm building my first site using CakePHP, and I like it so far. I've got a DB setup, initial data loaded, and a few models, views, and controllers to interface with the data.
I've created a globally accessible function to create Add/Edit/Delete type image links in various areas. It will be used across multiple views, so I need it accessible, essentially, everywhere. The function is defined in /app/config/bootstrap.php. I was hoping to use the HTML Helper's $html->image() and $html->link() methods to facilitate this, but they're not available in bootstrap.php and I'm not sure how to load/access the HTML Helper where I've defined my function.
Questions:
1) Is this a reasonable/idiomatic place to define a function of this sort?
2) If this isn't the correct place to define the function, where should I define it?
3) If this is the correct place to define the function, how can I go about loading various CakePHP helpers?
Again, I am new to CakePHP, so please let me know if my question is unclear, and forgive my ignorance. I've read/searched through a fair amount of the CakePHP documentation and while I can find plenty of references to loading helpers within Controllers via App::import(...); or $helpers = array(...);, I do not seem to have access to the App object and the $helpers member is specific to the AppController class, I assume. I assume I'm going about this incorrectly, so please help me understand the Cake way of accomplishing this.
No, that is not the correct place for such a function (or more accurately, it goes against the MVC paradigm). A better approach would be to create your own helper for the function. Depending on the complexity of the links you could also use elements.
As a rule of thumb only functions that are completely independent of anything else in the app should be in bootstrap.php, and even most of those would often be better somewhere else.

Resources