Reason for using array notation when defining AngularJS Controller - javascript

Apologies if this question sounds too obvious.
I've recently starting exploring and learning AngularJS. I've gone through some good tutorials -
Official Docs
w3cSchool
Some cool site
.. and there are a few other that I've seen.
I'm not saying that I've read/studied all the documents.
Question starts here -
Now, coming to the question, I see that the definition of a Controller is different in one place and it's different in some other -
One definition uses a sort of array notation (not sure of the official term) for injection:
app.controller("MyCtrl", ['$scope', function($scope){
$scope.someData = "Array notation";
}]);
And there's this, with no array:
app.controller("MyCtrl", function($scope){
$scope.someData = "non-array notation";
});
Not saying this is the only thing that I'm trying to understand but yes, I'd definitely love to understand the difference.
Is there a major difference between the two?
Thanks a lot.
Note: I did search for similar questions in SO but couldn't find what I was looking for. Sorry.

The difference is that when the second is minified, the parameter name will be minified and angular will no longer be able to inspect the arguments to figure out which dependencies to inject. The array syntax with the dependency in a string means that it is minification safe.
There is a library called ng-annotate which will change the second example into the first example so that the code is again minification safe.

There is not much difference between the two approaches. Both code works same way. But if you use the second code then it will confuse you after you minify your code.
Look for an example:-
app.controller("MyCtrl", function(a){ ... });//$scope is changed to a
And your code won't work as AngularJs code uses $scope variable as it doesn't take first, second, third, and so on parameters.
So, the first code is safer than second as if when you minify the code, it will still takes same variable i.e. $scope.
Look for an example:
app.controller("MyCtrl", ['$scope', function(a){...}]);//a refers to $scope
So, the above code works fine when you minify the code as $scope is injected in place of a.So, if you pass multiple parameters then ordering matters in this example.
Look at the following:
app.controller("MyCtrl", ['$scope','$timeout' ,function(a,t){...}]);
where a is injected as $scope and t is injected as $timeout.
So if you change the orders of parameters passed as
app.controller("MyCtrl", ['$timeout','$scope', function(a,t){...}]); where a is $timeout and t is $scope.
So, ordering matters in this example but in your second example code ordering won't matter as name matters like $scope, $timeout.
There's also another way to inject variables if you use your first example code like below:
MyCtrl.$inject = ['$scope'];
For multiple parameters,
MyCtrl.$inject = ['$scope','$timeout'];
So, there are mainly three kinds of annotation:
Implicit Annotation - your first example code
$inject Property Annotation - the $inject method
Inline Array Annotation - your second example code
You can learn more about it here

There is a difference when it comes to minification. If you were to minify your file, as you may do to increase performance this is when you may run into issues if you used option two.
Since Angular infers the controller's dependencies from the names of arguments to the controller's constructor function, if you were to minify the JavaScript code for the controller, all of its function arguments would be minified as well, and the dependency injector would not be able to identify services correctly.
So in essence you are better off using the first option, small bit more typing involved but it's safer and will not break if you minify your code :-)
This is quite a short tutorial but explains it nicely.

I have a great and complex AngularJS project, so making manually the changes the all the codes would have been a big pain. But I solved using babel-plugin-angularjs-annotate.
Install the plugin:
$ npm install babel-plugin-angularjs-annotate --save-dev
and then add the plugin to the .babelrc file:
{
"presets": ["#babel/preset-env"],
"plugins": ["angularjs-annotate"]
}

Related

Angular js works on jsfiddle but not on my machine Strange ERROR

i am a beginner when it comes to angular
so here is the fiddle
http://jsfiddle.net/prantikv/knc6vrd9/1/
i have an simple app and as you can see i am just trying out the basics.
The example works fine on jsfiddle but when i run it on my machine i get a huge piece of error staring like this
Error: [ng:areq] Argument 'SimpleCont' is not a function, got undefined
And the ng-repeat doesnt show any output and the text input also doesnt work as well
i have run the page via a local wamp server as well and get the same result
Ommit creating a function, since angularjs is modular and provides you mechanism to create controllers, which can be used in applications.
So in your code, instead of:
function SimpleCont($scope){
$scope.nameList=[
{firstname:'john'},
{firstname:'jane'}
];
}
Create module and controller within it. First use module method from angular, which takes as first parameter name of module ( later to include in ng-app ) and as a second parameter dependency list, which in this situation is empty.
angular.module('myApp', []).
Then invoke controller function on module.
Module method always return itself, so you can add later another contorllers by using dot ..
controller('SimpleCont', function(){
this.nameList=[
{firstname:'john'},
{firstname:'jane'}
];
});
This is code instead of function, this code sets module and assign controller to it.
In your application to use module and created controller within it, set ng-app properly.
instead of:
<div ng-app>
use:
<div ng-app="myApp">
Generally good to know how to create controllers and modules in angularjs for beggining, because later you can learn other curious things like services, factories and also get to know what is $http service and how to use it for making ajax calls.
Also good to automate work thanks to grunt, karma and yeoman.
Here is good tutorial to start.
Here is about yeoman a tool you can use to work with angular.

Angular services and browserify

I am using AngularJS for a SPA, and I am using browserify to build my application. Now the question came up whether we should write Angular services in the classical way, or simply require them.
As Angular services are singletons, this could be easily done with require as well: Simply expose an object literal, and you're done. Factories are also possible, simply expose a function. And so on ...
The only disadvantage I can currently think of is that I am not able to access other real Angular services from such a file (like, e.g., $http), but with browserify in the background this does not seem to be that important. E.g., you could easily use Node.js's http module for that, thanks to browserify.
So what do you think of this? What are other advantages and disadvantages for this?
PS: Please note that I'm not asking for whether this is good or bad, as this is probably mainly subjective. I'm rather interested which opportunities appear, or which risks I have to deal with.
One disadvantage to doing this is writing unit tests. It will be difficult to mock your dependencies if you are simply requiring them rather than using Angular's dependency injection.
This is somewhat of a dealbreaker for me because one of the many benefits of using Angular is the testability of the framework.
It's bad.
Just use browserify to initially load in all the modules you need.
you miss out on $httpBackend
your code becomes harder to follow, ie, there is very rarely a point to reuse that directives controller
you miss out on $http interceptors
you miss it in being able to modify and interact with other injectables.
The only thing I'd use browserify/webpack/requirejs for in an angular app is two things:
creating js bundle
injecting templates as strings into the angular template cache as a module.
Personally this kind of approach is just a pointless complication.
If you require things like $http you won't have any way to inject dummy/mocks of those services during testing.
Although somewhere you will do the wiring between your super-duper service that needs $http and the place you need it.
The first thing that came into my mind is resolvers in routes. You can even have some helper methods to deal with declaring the same dependencies many times.
Imagine this module:
function SuperResource($http, pathExpression) {}
exports.SuperResource = SuperResource;
exports.superResourceFactory = function(pathExpression) {
return [
'$http',
function() {
return new SuperResource($http, pathExpression);
}];
};
Somewhere you will do:
var myModule = require('./myModule.js');
resolvers: {
usersResource: myModule.superResourceFactory('/users')
}
Or even you could have a user modules that defines the user resource:
var myModule = require('./myModule');
exports.userFactory = ['$http', function() {
return new myModule.SuperResource($http, pathExpression);
}
Yes, these are angular specific helpers in an otherwise angular free code, but at least they're isolated in their own method/name.

AngularJs Load dependencies files

I'm learning AngularJS and in all tutorials and screencasts I always saw to use angular writing code all in a unique file, example directives, controllers, factories etc...
Logically for large applications, you will split out the code, make it maintainable and flexible in multiple files and also we should be careful about how many <script> tags we have to require to let our JavaScript files run correctly.
I would like to know which is the best practice to require files when needed, importing less javascript files possible in my view. I took a look at RequireJs but it seems a bit complicated to use it. Is there some tool more efficient and easy to use? Or any good resource to get started?
A small example can be that I have a sort of plugin that has been built using directives, controllers and factories:
app-|
--Controllers
|_ pluginController.js
--Directives
|_ pluginDirective.js
--Factories
|_ pluginFactory.js
Instead of requiring all three files how do you make it work?
Here' a great example of how to use RequireJS and AngularJS together. It's a fork of the Angular Seed project and it should hopefully point you in the right direction. It comes with RequireJS baked right in. I definitely recommend learning RequireJS!
I would advice you to read up on dependency injection in the Angular documentaion. It all depends on how you set things up to be honest. If you want to use your service/factory in your controller then you would add the factory as a dependency in your controller or directive. See example below :
Angular.module('{YOUR MODULE NAME}').controller('{YOUR CONTORLLER NAME}', ['$scope', '{FACTORY NAME}',
function($scope,{FACTORY NAME}) {
}]
To invoke the directive within your controller, you would simply could simple add the directive to your controller template. This is a basic example, to learn more read about dependancy injectioninvoke
To be clear that I understand - e.g. I want to use "angularFileUpload" module, I need to add it to my module dependency list -
angular
.module('kids', ['angularFileUpload'
])
and load the script?
<script src="angularjs/plugins/angular-file-upload/angular-file-upload.min.js" type="text/javascript"></script>
Thanks for help.

Do javascript obfuscators work for AngularJS?

There are javascript obfuscators around like http://www.javascriptobfuscator.com/Default.aspx. They work on simple javascript code. But would they work on more complicated front-end AngularJS code which may have several files for controllers, services, modules?
What tools do the experienced programmers on StackOverflow use for obfuscating their AngularJS code? Or you don't at all because it is impossible to obfuscate front-end code?
You can use tools like Uglify or the Closure Compiler to minify and obfuscate AngularJS code, but it can get tricky because of Angular's ability to inject dependencies based on the name of the variable used (which will all be changed when you minify or obfuscate the code).
You'll need to use the array form of defining your modules, controllers, etc. It's explained in the "Notes on Minification" section in step 5 of the Angular tutorial: https://docs.angularjs.org/tutorial/step_05
Basically, if you're currently using the shorthand method of dependency injection, ie:
myApp.controller('myController', function($scope, $http) { ... });
you need to change it to the more verbose array based method:
myApp.controller('myController', ['$scope', '$http', function($scope, $http) { ... }]);
This way you're telling angular what objects to inject into your function using strings, which won't be changed during minification, instead of relying on names of the $scope and $http variables themselves.
There is a command line tool called ngmin that will automatically make these changes for you if you don't want to modify your codebase: https://github.com/btford/ngmin
The 'Conceptual Overview' section of the ngmin readme also has a good explanation of this problem.

AngularJS: Reference to controller dynamically (by name)?

Working on a module based app where depending on the user, I'll load a given template (view) as a module inside a common view. The problem is that the different views require different controllers and they one share a small set of common form inputs.
Based on a call to my server I'll get a JSON response containing what view/controller should be loaded for that user. This solution worked fine earlier as all my controllers were in the global scope:
$scope.corporation.payloadController =
// Contains the String "ComputerPayloadCtrl"
[window]data.corporation.payloadController;
Now however, after I have rewritten the applications to use the angular module design pattern, I get the following error (I no longer use [window]):
Argument 'corporation.payloadController' is not a function, got string
The controller is already defined, so I'm only looking for a way to reference it by String.
.controller('ComputerPayloadCtrl', ['PayloadService', '$scope',
function(PayloadService, $scope) {
$scope.payload = PayloadService.payload;
}])
The more I work with this problem, the more this entire approach I've chosen is bugging me. So if anyone has any suggestions on how to alternatively solve this I'll gladly hear it.
Edit: So I found a very simple workaround, but I'll let the question stand in case there is an actual way to do this.

Categories