Suppose that I have a global constant that need to be accessible in every angular module, what is the advisable approach to declare the constant. I have three approach in my mind but i not sure which to be used.
Appreciate if anyone could point out what is the benefit using Approach 2 compare to Approach 1 in this condition.
Approach 1 (pure js constant):
var jsConstant = {
constant1: 'constant1',
constant2: 'constant2'
};
angular.module('mainApp').controller(['$scope', function($scope) {
$scope.constant1 = jsConstant.constant1;
$scope.constant2 = jsConstant.constant2;
}]);
Approach 2 (pure angular constant)
angular.module('mainApp').constant('angularConstant', {
'constant1': 'constant1',
'constant2': 'constant2'
});
angular.module('mainApp').controller(['myConstant', '$scope', function(myConstant, $scope) {
$scope.constant1 = angularConstant.constant1;
$scope.constant2 = angularConstant.constant2;
}]);
Approach 3 (mixture of js and angular constant)
var jsConstant = {
constant1: 'constant1',
constant2: 'constant2'
};
angular.module('mainApp').constant('angularConstant", {
'constant1': jsConstant.constant1;
'constant2': jsConstant.constant2;
});
angular.module('mainApp').controller(['myConstant', '$scope', function(myConstant, $scope) {
$scope.constant1 = angularConstant.constant1;
$scope.constant2 = angularConstant.constant2;
}]);
Advisable way is to use constant:
(function(angular, undefined) {
'use strict';
angular.module('myApp.constants', [])
.constant('appConfig', {userRoles:['guest','user','admin']});
})(angular);
Even better way is to inject those values on every build from the server, since most of the time you want to define those values on your server and forget about managing them in other places. For that purpose take a look at ng-constant module for grunt or gulp
Edit
Approach 3 is just a messy version of Approach 2 with unnecessary declaration of JS variables outside of Angular modules
Approach 1 is not good, because you those constants are not really reusable. If you have another controller that wants to reuse those values?
In approach 1 you created a global variable jsConstants which, I believe, is not treated as a good practice - you should avoid creating global constants if possible. Moreover - properties of jsConstants are not really constant - anyone, anywhere can change them. Also, using global constant makes controllers less testable (unit), as they will depend on this global object.
In approach 2 I believe you are not allowed to change these values, so they more act as constant values. You inject them in controllers, so they are still unit-testable.
Related
I'm new in AngularJS and I'm doing a refactor of an AngularJS application and I noticed that there is a single controller file with a lot of functions that manipulate and set scope variables.
Following an example:
test.controller('testCtrl', function testCtrl($scope) {
$scope.init_filters = function() {
$scope.filter_1 = [];
$scope.filter_2 = [];
$scope.filter_3 = [];
$scope.filter_4 = [];
$scope.filter_5 = [];
};
$scope.showChanges = function() {
if ($scope.item_list.length > 0) {
$scope.messages = [];
for (var i = 0; i < $scope.item_list.length; i++) {
$scope.messages.push($scope.item_list[i].message);
}
$scope.another_function();
}else{
// other stuff
}
};
//other functions like these
}
So, I would like to split these functions in multiple JS files. I searched about this problem and I found that in a lot of case is used a service. But I think that this is not my case, because I need to working on directly on the controller's scope.
I mean, I don't want a separated function that get as parameters some scope variables and return the variable.
So, what is the best practices for doing something like this? is it possible?
If you want to use multiple files then split the definition to multiple files by passing the scope to another method and then define the rest of methods there.
File1
app.controller('CtrlOne', function($scope){
app.expandControllerCtrlOne($scope);
});
File2
app.expandControllerCtrlOne = function($scope)
{
}
Check this video
As you said the code you found for controller is large one so there are multiple ways in angular js that you can implemented the separation of code.
I will suggest you to go with following approach:
Use service to add those code in it which you need in other places as well and you know that this code does not require scope object..
Use factory to add some Utility kind of functions. The collection of logic which does not require scope object again...
As controller code is too large, I think View/UI of same also being as per wrote...
So for this you can go with creating directives for section in view..
Where-ever you think this peace of view section can be separate and standalone logical functionality that you can move into directive.
There are three ways to create directive with scopes:
A. Shared Scope B. Isolated Scope C: shared and Isolated scope
In this ways may you can at-least make your controller code readable and looks modular.
Let say::
module.controller('longRunController', function() {
#TYPE 1 code
// some code which fetch dat from API
// some code which save variable and objects which can used in another controller or directives
// some code which need to passed to other controller even after route changes
#TYPE 2
// some code which is only related this controller but has some bussiness logic
// some code which is a kind of utility functino
#TYPE 3
// all $scope related variable $watch, $scope and related variables
// some code of perticular section of which VIEW which handle by this controller
});
Consider in above patter your controller code has:
So type 1 code can be moved to Service
type 2 code can be moved to factory
type 3 code can be move to directives
you can pass $scope as a parameter to the external function.
And because you just use the objectreference, all changes you made in your external functions are made on the $scope object from your controller.
test.controller('testCtrl', function testCtrl($scope)
{
showChanges($scope);
});
...
function showChanges(scope)
{
scope.param1 = "Hello World";
}
I'm new to AngularJS and I read you can declare function in 2 different ways (perhaps more...):
First:
var myApp = angular.module('myApp', []);
myApp.controller('mainCtrl', function($scope){
$scope.message = 'Yes';
})
myApp.controller('anotherCtrl', function($scope){
$scope.message = 'No';
})
Second:
var myApp = angular.module('myApp', []);
myApp
.controller('mainCtrl', mainCtrl)
.controller('anotherCtrl', anotherCtrl)
function mainCtrl($scope){
$scope.message = 'Yes';
}
function anotherCtrl($scope){
$scope.message = 'No';
}
Using the first method I was able to use different files (i.e.: controllers.js with all the Controllers, directives.js with all directives, etc...).
I tried using the second method and gives error if functions are declared in different files, which make sense because they are called in one file but . On the other hand it's more readable to me as there is less nesting and so forth.
What is the difference?
What is the difference?
Your First Example
In the first example, you're creating the functions via function expressions as part of your calls to myApp.controller.
It also happens that in your example, the functions are anonymous (they don't have names), but you could make them named if you wanted (unless you need to support IE8 or IE legacy modes that equate to IE8 or earlier):
myApp.controller('mainCtrl', function mainCtrl($scope){
// Gives it a name -------------------^
$scope.message = 'Yes';
});
(This article on my anemic little blog explains why there are issues with that on IE8 and earlier.)
Since the functions don't have anything referring to them except whatever .controller hooks up, you can't use them elsewhere unless you can get references to them back from myApp, or if you declared a variable and assigned it within the expression making the call:
var mainCtrl;
// ...
myApp.controller('mainCtrl', mainCtrl = function mainCtrl($scope){
$scope.message = 'Yes';
});
// ...you could use the `mainCtrl` variable here if you needed
// to reuse the function
Your Second Example
In the second example, you're creating the functions via function declarations, and then referring to those functions in your calls to myApp.controller. The functions have names (they're not anonymous). You could use those functions in more than one place, if it made sense to, without doing the variable thing shown above.
In your second example, you could declare the functions in separate files, but in order to use them in your call to myApp.controller, you need to get a reference to them somehow. There are a large number of ways you can do that, from RequireJS to SystemJS to ES2015 modules to Angular modules (I think) or any other AMD mechanism.
I have a global variable declared outside of angular context:
var globalv = "Hello";
This variable does not change. I have a directive that wants to use it...
$scope.somevar = globalv + "_";
What is the simplest way to include the globalv in my angularjs app/context? When I attempt to reference it in the above manner, I get an error.
I would consider wrapping the value in an angular constant which will enable you to inject it only where its needed
var myApp = angular.module('myApplication',[]);
myApp.constant('myConstant', globalv);
Just ensure that the globalv variable is defined before your angular module is defined.
I like this better than using a rootscope/global variable since you can control who the consumers of the value are.
You can inject the constant like so:
mayApp.directive('myDirective',['myConstant',
function (myConstant) {
return {
..
};
}]);
Is there any reason why you couldn't include it in the angular context? If it absolutely needs to be global you can add it to the rootscope within the angular context.
/* declare rootscope variables when the angular app starts*/
angular.module('someApp').run(function ($rootScope) {
$rootscope.globalv = "Hello";
});
You can then reference that rootscope variable anywhere within your angular app.
This is pretty simple to me, but I personally hate using $rootScope unless I have to. You should really try and get away from global variables.
I'm going through this Angular tutorial and have noticed that variables seem to be freely added to functions as needed. I figured up to this point that there were just conventions but am I mistaken? Consider the last file:
app.controller('PostsCtrl', function ($scope, $location, Post) {
$scope.posts = Post.all;
$scope.post = {url: 'http://'};
$scope.submitPost = function () {
Post.create($scope.post).then(function (ref) {
$location.path('/posts/' + ref.name());
});
};
Here "$location" was added to function() between $scope and Post. Is $location the only option for the 2-nd parameter in an anonymous function here with 3 parameters or is angular somehow looking at the name of the 2nd parameter and deducing that it needs to inject $location there? Where in the documentation can I see all the conventions for 1, 2, 3, etc parameter versions of this function?
This code doesn't appear to work btw. Post is undefined.
With angular, the names are significant; in plain javascript, not really.
With that, however, if you wanted them to be insignificant in the example above, you could do:
app.controller('PostsCtrl', ['$scope','$location', 'Post',
function (foo, bar, foobar) {
....
}
]);
In which case you're mapping the first, second, and third parameters to $scope, $location, and Post respectively. This is actually a better way to do this, as when you minify with angular, it is going to change the names of those parameters, and it is going to break it.
In this case, you could order those parameters however you'd like. Angular uses Dependency Injection to supply dependencies to your controller. In this case, it will infer those dependencies based on the names so you can order them however you'd like.
Unfortunately, using this method with minification is impossible. In that case, you need to specify your dependencies. Your controller function must then take them in the order that they are defined:
app.controller('PostsCtrl',
['$scope','$location','Post', function($scope, $location, Post) {
}]);
Ahh I was confused by this at first as well. Angular has a nice feature called Dependency Injection. The names do matter because angular looks at the names of the parameters and decides what object to pass to the function. In this rare case in JavaScript, names matter but order does not.
Since you already have other answers with Angular specific instructions, I'll try to explain in simple code how Angular achieves dependency injection in JavaScript and how one might go about doing something similar in plain JS.
First it turns the function into a string, then reads the parameters, and extracts the property from the dependency container, and finally calls the function with the parameters that were extracted. Here's a very simple example of how one might do this:
// parses function and extracts params
function di(app, f) {
var args = f.toString()
.match(/function\s+\w+\s*?\((.*?)\)/)[1] // weak regex...
.split(/\s*,\s*/)
.map(function(x){return app[x.replace(/^\$/,'')]})
return function() {
return f.apply(this, args)
}
}
// Example
// A dependency container
var app = {
foo: 'this is foo',
baz: 'this is baz'
}
// this is like adding a function with the Angular ecosystem
// the order of arguments doesn't matter
var test = di(app, function test($foo, $baz) {
console.log($foo) //=> this is foo
console.log($baz) //=> this is baz
})
// No need to pass arguments, they were injected
test()
//$ this is foo
//$ this is baz
But handling DI by stringifying the function has drawbacks; old browsers don't support Function.prototype.toString very well, and minifiers will shorten the variable names making this technique useless. But Angular can get around this problem by injecting strings and parameters that match those strings; minifiers won't touch the strings.
Obviously AngularJS does much more than this, but hopefully it will clear your mind on "how the hell is this possible?!" -- Well, it's kind of a hack.
The answer here is really Angular specific. You want to read about Dependency Injection here:
https://docs.angularjs.org/guide/di
The Implicit Dependencies section should be good reading. In short, the name does matter for Angular but the order does not. This is NOT the case for raw JavaScript however.
From the Angular site:
There are three equivalent ways of annotating your code with service name information:
Implicitly from the function parameter names
Using the $inject property annotation
Using the inline array annotation
I am trying to re-write an application using angular.js, but I still do not see how it works (more or less).
For instance. In my previous code, I had a function that was executed once everything was loaded that initialised variables, accessed for everyone in a window.variable style.
Now I want the same here.
My idea was to build a factory to return an object with all the variables, and then make that everyone had access to this object (somehow).
The questions are:
1- Am I right? I should initialise variables through a factory?
2- And how can I "call" this factory-method at the beginning of the code? with the module.run function?
Cheers,
I'd probably put the variables in a service, and then inject that into a wrapping angular controller. Any other controllers that you would want to have access to these 'global' variables would be nested under the wrapping controller, thus inheriting the variables.
var app = angular.module("app", []);
app.service("globVars", function () {
var vars = {};
vars.someVar = "a";
vars.someOtherVar = "b";
return vars;
});
app.controller("WrappingCtrl", function ($scope, globVars) {
$scope.globVars = globVars;
});
app.controller("NestedCtrl", function ($scope) {
console.log($scope.globVars.someVar); // => "a"
});
<html ng-app="app">
<div id="wrapper" ng-controller="WrappingCtrl">
<div class="nested" ng-controller="NestedCtrl">
{{globVars.someVar}}
</div>
</div>
</html>
I think you should avoid using global variables as much as you could
But if you have to use them, you are right, you can initialize them in module.run and add the module in the app dependencies
If you want to access some variables, you must see to $rootScope, because you can access $rootScope everywhere.
Also you must use angular config and run functions.
I advice to you don't use global scope, because it affects on app speed, memory leaks etc.