I am using gulp-ng-config in my project to dynamically generate some constants at build time.
This is working just fine, but it got me and a colleague thinking about how constants are accessed in an angular app.
If you want to access constants in controllers that are defined on the same module that the constants belong to, do you still have to inject the constants by name to the controllers so you can access their values?
Looking at various examples, I'm pretty sure this is the only way - just wondered if there was some other way that didn't require a further dependency to be injected.
The constants injection is part of a best-practice-approach.
It's just a syntactical overhead preventing you from doing bad stuff like defining global variables and using them all over the place.
Of course you still can go ahead and define variables on the global window-scope and use them everywhere, but i cleary wouldn't suggest to do so.
So you can do
window.foo = 123;
and somewhere else
alert (window.foo);
this will show the wanted value but, of course, this is very bad practice.
You can also define variables on the angular $rootScope and use them anywhere. Yet, bottom line this is nothing else than defining them on window scope and shouldn't be done as well.
The last option (and probably the best for your scenario) is to define one constant OBJECT containing some more values. This way you just have to inject one constant object instead of many different ones.
Say you have two constants so far
$width = 1024
$height = 768
there is nothing bad about defining one injectable constant
$config = {
width: 1024,
height: 768
};
if you just want to access the $constant from outside the angular world you can always do it like this:
angular.element('body').injector().get('$constant')
assuming you declared your ng-app on the 'body' element or even further outside (like the 'html' element).
This is what dependency injection is made for : to force you to show all dependencies to your model.
Otherwise you can use the injector and use the method $get to get the values but i don't recommend it.
A last way would be to use a provider to store all constants and inject only the resulting service. However this will mask from which module each constants come from.
Related
Some of my code got a review comment saying something like "move const outside the function to avoid redeclaration". This was a normal function component, something like this:
export default function someComponent() {
const someString = 'A string';
///...
}
I was confused about the idea of this causing a redeclaration, because it does not, I know that the record that holds the variables and constants belong to the scope, so it's not exactly that.
But then i remembered that typescript does not allows you to have a const inside a class, not sure about the reason or is this is related. But then ts added the readonly modifier in ts v2, so the confusion remains.
Should const be outside function components, or not?
I would love to know more opinions.
There are 2 sides to the coin. Firstly, in terms of clean code and readability, I strongly prefer local declarations like in your example. I would love using more nested functions as well.
However, in JavaScript, every time the function executes, the local definitions will be redeclared, even if they are constants or functions. So it's a trade-off. If the function is called many times, this becomes an overhead.
I think it would not be hard for a compiler like TypeScript's tsc or some other pre-processor to extract those definitions at compile time to have best of both worlds. But it's likely that they do not do this to remain fully compatible. I am not aware of such tools but would be interested if there are some.
I know i can use $injector.has('<constant name>') to get a constant by name but the problem is i don't know the name ahead of time, i just want to get a list of all angular constants that (for example) start with "json_". The reason i need this is because i'm building a module that people can plugin to their own code, this is why i won't know the names ahead of time but i can at least ask the programmer to start the constants i need to work with, with "json_". Currently i have to tell them to name their constants exactly "json1", "json2", "json3", etc and this is bad because the user has to keep track of where they are in numbering and it's not good design. There doesn't seem to be anything directly within the API's to do what i'm trying to do.
There is no built-in way to get all constants in an angular module, but you can achieve this by loop through _invokeQueue which an internal using array to hold all registered services on a given angular module. If you registered a constant called json_obj in angular.module('app'), then in angular.module('app')._invokeQueue should contain an array like:
['$provider', 'constant', ['json_obj', valueObj]]
So you can get a list of json_* constants by:
function getJsonConstants(){
var queue = angular.module('myApp')._invokeQueue;
var jsonConstant = [];
angular.forEach(queue, function(item){
if(item[1] === 'constant'){
if(item[2][0].match(/\bjson_/gi)){
jsonConstant.push({
key: item[2][0],
value: item[2][1]
})
}
}
})
return jsonConstant;
}
One more thing, this function can only return all constant registered in angular.module('myApp'), if myApp has any dependent modules, you should loop all _invokeQueue in those modules to get completed constant list.
There are several Ruby C API functions for running some Ruby code. Most just run the code in an isolated binding like require does. But some of them first wrap the code in an anonymous module before running it. For example, rb_load takes an argument for whether you want this wrapping, rb_eval_string_wrap is just rb_eval_string_protect but with wrapping.
In C, the wrapping looks like this:
/* load in anonymous module as toplevel */
th->top_self = rb_obj_clone(rb_vm_top_self());
th->top_wrapper = rb_module_new();
rb_extend_object(th->top_self, th->top_wrapper);
What is the point of doing this? I've tested these functions alongside their unwrapped equivalents and the result is always the same. Is there some use-case I'm not seeing?
I should've done some more testing. It looks like this is a bug.
The point of wrapping code in an anonymous module is to not pollute the toplevel namespace with constants/methods defined in the code. rb_load does this wrapping properly, rb_eval_string_wrap does not.
As far as I know assigning this to a variable is used within callbacks where the this scope may change. But digging through the ExtJS source I found it used in all sorts of functions but not always. So is there any reason that I would assign this to a local variable beneath the scope or is the ExtJS source just struggling with different developer styles?
#kevhender pointed me to the right sencha forum thread where evan has given a very good explanation.
It's only for the size. And here's a example:
function doA() {
var me = this;
me.a();
me.b();
me.c();
me.d();
me.e();
me.f();
me.g();
me.h();
me.i();
me.j();
me.k();
me.l();
}
function doB() {
this.a();
this.b();
this.c();
this.d();
this.e();
this.f();
this.g();
this.h();
this.i();
this.j();
this.k();
this.l();
}
Compressed we get:
function doA(){var a=this;a.a();a.b();a.c();a.d();a.e();a.f();a.g();a.h();a.i();a.j();a.k();a.l()}
function doB(){this.a();this.b();this.c();this.d();this.e();this.f();this.g();this.h();this.i();this.j();this.k();this.l()};
It adds up.
According to that we should
NOT use a local var if we use this only up to three times
function doA(){var a=this;a.a();a.b();a.c();};
function doB(){this.a();this.b();this.c();};
and use it if we use this more often then three times
function doA(){var a=this;a.a();a.b();a.c();a.d()};
function doB(){this.a();this.b();this.c();this.d()};
There are a few reasons for this, the most significant being that using a local variable will save a few bytes during compression of the files. It may not seem like much for a small bit of code, but it can add up a good bit over time.
There is a long thread at the Sencha forums talking about this very issue: http://www.sencha.com/forum/showthread.php?132045.
As you've correctly stated this is sometimes used in places where the scope might change.
There are cases where you want to do the same to make sure there are no scoping issues.
Other than that, the devs sometimes just assign this to a variable out of habit more than out of necessity.
I used the implicit method for retrieving data objects:
setData = function(segment){
var url = 'https://myFireBase.firebaseio.com/';
var rawData = angularFire(url+segment,$rootScope,'data',{});
rawData.then(function(data){
// sorting and adjusting data, and then broadcasting and/or assinging
}
}
This code is located inside a service that gets called from different locations, by development stages it'll probably be around 100 - 150 so I got out of the controllers and into a service, but now firebase data-binding would obviously over-write the different segments so I turned back to explicit methid, to have the different firebases only sending the data to site instead of data-binding and over-writing each other:
var rawData = angularFireCollection(url+segment);
And right there I discovered why I chose the implicit in the first place: There's an argument for the typeof, i could tell firebase if I'm calling a string, an array, an object etc. I even looked at the angularfire.js and saw that if the argument is not given, if falls back to identifying it as an array by default.
Now, I'm definitely going to move to the explicit method (that is, if no salvation comes with angular2.0), and reconstructing my firebase jsons to fit the array-only policy is not that big of a deal, but surely there's an option to explicitly call objects, or am I missing something?
I'm not totally clear on what the question is - with angularFireCollection, you can certainly retrieve objects just fine. For example, in the bundled chat app (https://github.com/firebase/angularFire/blob/gh-pages/examples/chat/app.js#L5):
$scope.messages = angularFireCollection(new Firebase(url).limit(50));
Each message is stored as an object, with its own unique key as generated by push().
I'm also curious about what problems you found while using the implicit method as your app grew. We're really looking to address problems like these for the next iteration of angularFire!