Recall the method signature for angular.module. If the second parameter, requires is provided, then we are creating a new module instead of retrieving an existing one. From all the documentation and examples I've seen, this parameter is always passed an empty array when used. My question is, is requires meant to be used for anything else besides telling Angular to create a new module instead of getting an existing one? What would happen if I instead passed it a non-empty array? Are those values used for any other purpose? Links with solutions are much appreciated. Thanks.
requires meaning an array of modules which your module depends.
example:
moduleA.js
var customModule = angular.module ('ModuleA');
// controller, services, factories , etc codes here
app.js (main app)
var app = angular.module ("app", ["ModuleA"]);
if I just use:
angular.module ("app");
It means that i'm just retrieving the module named "app". Which is useable when controllers or directives or factories is defined in a different JS files and you want to configure it to the module "app"
The second parameter is used to define the module's dependencies - i.e., a list of modules (module names, to be precise) that should be already loaded by the injector before the current module is loaded.
And here's how this param (stored in the module's requires property) is used: (injector.js/loadModules()):
var runBlocks = [], moduleFn, invokeQueue, i, ii;
forEach(modulesToLoad, function(module) {
if (loadedModules.get(module)) return; // skipping already loaded modules
loadedModules.put(module, true);
if (isString(module)) {
moduleFn = angularModule(module); // prepared module object
runBlocks = runBlocks.concat(loadModules(moduleFn.requires))
.concat(moduleFn._runBlocks);
// ...
}
// ...
}
return runBlocks;
As you see, this property can be used to set up a hierarchy of dependencies as well (when ModuleFoo depends on ModuleBar depending on ModuleBaz).
Related
In our application we load requirejs, which in return loads angularjs, and also other javascript modules. I am wondering if there any way to get hole of these LOADED modules (angularjs, javascript modules) in protractor test. Note, we want the instance that is loaded by the browser when running Protractor, we don't want to create instance by ourselves.
Any suggestion or example?
Thanks in advance.
Nick Tomlin's answer is what you can do if a module returns serializable data structure as a value. You call require and call with the module's value the callback that executeAsyncScript gives you to allow returning asynchronous values. This will work, for instance, if your module returns "foo" or { foo: 'bar' } or structures that are generally serializable.
However, it won't always work. Complex modules cannot be retrieved that way. Roughly speaking you should expect what you send through executeScript and executeAsyncScript and what they return to have the same limitations as JSON.stringify does. One major exception is that Selenium will wrap DOM objects returned from these calls into a structure that allows to identify them on the script side, and that allows passing them back to the browser. (Then again, there are limitations there too. This is why you get stale element exceptions, for instance.)
If you try to retrieve modules that export functions, you'll probably get something but it won't be complete. Try this, for instance:
browser.executeAsyncScript(function () {
arguments[0]({ foo: function () {}});
}).then(function (value) {
console.log(value);
});
The output I get is:
Object { foo: Object {} }
The function has been turned into an empty object.
I do not use angular with require.js, but i'm assuming you could access the require'd angular the same way you would in a module:
var pageAngular = browser.driver.executeAsyncScript(function () {
var callback = arguments[arguments.length - 1];
require(['angular'], function (angular) {
callback(angular);
})
});
The use of executeAsync is necessary here, since AMD modules are loaded asynchronously.
Do note that as #louis noted, the return of executeAsyncScript is going to be a serialized object, not the "live" instance of angular. If you need to interact with angular within the context of your page, you should do so within the callback torequire.
Something like this should do it:
var angular = browser.driver.executeScript("return window.angular;");
I am trying to set up some app wide constants from a http endpoint. I don't want (or need ) to do it via the manual bootstrap way (ala this ). Ideally I want to load the constants in after the user has signed in. So I assume I need to define the constants somewhere I can run some code (and use $http). But whenever I define a constant inside a controller or a config block, the constant gives an unknown provider error when I pass it into another module. As soon as I move the definition outside of the controller it works. So for example, if I have ...
var app = angular.module('testApp',[]);
app.constant('test', 'test value');
then I can pass that into another module's controller like this:
var app2 = angular.module('anotherModule',[testApp]);
app2.controller('TestCtrl', ['test',
function(test) {
console.log(test)`
}..
and this will output 'test value' as you would expect. But if the constant is defined inside a code block then it seems it doesn't register as a provider. So, for example:
var app = angular.module('testApp',[]);
app.controller('firstCtrl',function(){
app.constant('test', 'test value');
}
If I run a page with that controller, the constant seems to register (in the sense that it is listed inside the _invokeQueue array on the testApp module) but the injector service doesn't have a provider for it and I get an unknown provider method for it on anotherModule.
I initially felt that a factory or service was overkill for what I was trying to do but maybe that is the way I should go. But I would also love to understand why a constant defined like this isn't injectable.
Easiest way to load constants for HTML5 app:
add the following line into head section:
< script src="service/constants.js">
create web api / wcf / java REST service method with signature "service/constants.js"
in that method return "window.constants = {...}".
replace "..." with actual key:value pairs read from database.
I have a singleton that creates classes for me. Each class is in a separate file.
// singleton.js
angular.module('mymodule')
.service('singleton', SingletonClass)
// someclass1.js, someclass2.js, ...
// Multiple files with same layout. Each web page has different # of these files.
// Therefore, it's hard to inject them statically
// (ie, via aggregator service/factory).
angular.module('mymodule')
.??('someClass', function(singleton) {
classOptions = {}; // details here
// creates a class with options and adds it to `singleton`s cache
singleton.addClass(classOptions);
})
// yet another file
angular.module('mymodule')
.controller('myController', function(singleton) {
singleton.getClasses(); // gets all classes added by #addClass method
});
Basically, I want each individual "class" to be self-contained in its own file similar to how modules are self-contained. That means I don't want want singleton or any aggregator service/factory to know of the existence of these classes in its creation.
Edit:
I'd like to clarify: I'm using singleton instead of individually injecting the created classes because I don't yet know which classes will be created yet. The web page will dynamically load certain js files. Each file will inject the singleton object and use the addClass method to create a new class and store it inside the singleton object itself. The controllers using singleton.getClasses() won't know how many or which classes it's getting, but they have a uniform interface which allows it to use them in a predictable fashion.
You could try setting up providers for each class (per my comment above), though that seems . . . time consuming. Maybe you just don't need it? Make each class a separate file in a closure (like you use to need to do with jquery), and grab the singleton object from the injector:
// file 1 - Add other files for other classes.
;(function(window, $) {
$(function() {
var classOptions = {};
$('body').injector().get('singleton').addClass(classOptions);
});
})(window, jQuery);
This is not exactly the angular way, and angular purists will call for your head, but I think it will achieve what you're after if you don't want to mess with providers.
Using ngAudio, I cannot seem to get my code to work when I load an ngAudioObject within the controller. I am able to use the directives to work with my wav file directly in the view (so I know I have linked everything correctly) but not in the controller. I'm working with a number of modules and wondering if there is a conflict...
Please let me know if there is something obviously wrong.
var myApp = angular.module('screenApp', ['firebase','ngAudio']);
myApp.controller('screenController', ['$scope','$http','$firebaseArray','$interval', function($scope,$http,$firebaseArray,$interval,ngAudio) {
$scope.audio = ngAudio.load('sounds/dingding.wav');
...
See: https://docs.angularjs.org/guide/di#inline-array-annotation
When using this type of annotation, take care to keep the annotation array in sync with the parameters in the function declaration.
Your parameters and annotation array are out of sync. Add 'ngAudio' to the annotation array.
myApp.controller('screenController', ['$scope','$http','$firebaseArray','$interval', 'ngAudio', function($scope,$http,$firebaseArray,$interval,ngAudio) {
$scope.audio = ngAudio.load('sounds/dingding.wav');
I am trying to have external modules change my $translateProvider.translation on the main module. see this as a "tranlation plugin" for my app.
it seems like changing translations from the $translate service is not possible.
mymodule.service('MyService', function ($translateProvider) {
var lib = function () {
//EDITED FOR BREVITY
this._registerTranslations = function (ctrl) {
if (!ctrl.i18n) return;
for (var name in ctrl.i18n) {
/////////////////////////////
// THIS IS THE PLACE, OBVIOUSLY PROVIDER IS NOT AVAILABLE!!!!
$translateProvider.translations(name, ctrl.i18n[name]);
//////////////////////////////
}
};
//EDITED FOR BREVITY
};
return new lib();
});
anyone with a bright idea?
So, to answer your question: there's no way to extend existing translations during runtime with $translate service without using asynchronous loading. I wonder why you want to do that anyway, because adding translations in such a way means that they are already there (otherwise you would obviously use asynchronous loading).
Have a look at the Asynchronous loading page. You can create a factory that will load a translation from wherever you want.
I created an Angular constant to hold new translations. If I want to add a new translation, I add it to the constant. Then in my custom loader, I first check the constant to see if the translation exists (either a new one, or an updated one). If so, I load it from the constant. If not, I load it from a .json file (or wherever you load your initial translations from). Use $translate.refresh() to force translations to be reloaded and reevaluated.
Demo here
The demo is pretty simple. You would need to do a little more work if you wanted to just change a subset of the translations, but you get the general idea.
From the AngularJS docs (https://docs.angularjs.org/guide/providers):
You should use the Provider recipe only when you want to expose an API for application-wide configuration that must be made before the application starts. This is usually interesting only for reusable services whose behavior might need to vary slightly between applications.
Providers are to be used with the application's .config function. $translateProvider for configuration, $translate for other services and controllers.