Angular module config block never accessed - angularjs

I am new to angular and I'm trying to load/create an angular module using requirejs.
For some reason the config block never gets called.
I don't think requirejs is to blame here - it's only for the sake of completeness.
define(['./IdeaListCtrl'], function (IdeaListCtrl) {
var moduleName = 'clap.Idea';
console.log("Loading " + moduleName); // Printed
angular.module(moduleName, []).config(function () {
console.log("Config block accessed"); // NOT PRINTED
});
console.log("Loaded " + moduleName); // Printed
return moduleName;
});

Angular essentially lazy-loads its components so you need to bootstrap the module to the page or reference it elsewhere before it will run the config block.

Related

angular.injector.invoke makes my controller undefined

While learning angular, read a blog which elaborate that
we can access factory/service outside of controller using angular.injector() , But when I try this, it gives my main controller is undefined error and everything stops working.
see the working plunker with commented culprit code block.Below is main code of lines using angular.injector(["app"]).invoke
var app = angular.module('AlphaModule', []);
// service added
app.service('tea', function(){
return {
teaType: function(teaType) {
console.log('Service => Morning tea must be ' + teaType);
},
sayHello: function(msg) {
console.log('Service => Hello ' + msg);
}
};
});
// ERROR in console when uncomment this code block
angular.injector(["app"]).invoke(function(tea){
tea.sayHello('Yellow');
});
// main controller definition
var AlphaController = function($injector, tea) {
var vm = this;
vm.timeNow = new Date().getTime();
....
};
app.controller('AlphaController', AlphaController );
Please highlight what am I missing/ wrong doing here?
My Guess:
I also read that $injector are singleton . What exactly this means? can we use $injector only once! or may be this is the issue as i have used $injector in .controller also
Your module name mentioned is wrong. module name is actualy AlphaModule not app.
angular.injector(["AlphaModule"]).invoke(function(tea){
tea.sayHello('Yellow');
});

AngularJS: Module not found error when creating module from asynchronous function's callback

When i try to create angularjs module in usual way, it works perfect, but when i try to execute same code inside a callback function of aync function call, it throws error that module not found:
The following code works fine:
var myApp = angular.module('SSApp',[]);
myApp.controller('config', function($scope) {
});
But following throws error:
Init_Data(function() {
initApp();
});
function initApp() {
var myApp = angular.module('SSApp',[]);
myApp.controller('config', function($scope) {
});
}
function Init_Data(callback) {
chrome.storage.local.get(null, function(data) {
window.data = data;
callback();
});
}
I've defined ng-app="SSApp" directive in respective html code.
The reason your code is not doing what you expect is because, Angular tries to bootstrap the module "SSApp" automatically when the DOM is ready. But, finds no such module defined by your JavaScript code when it tries to do so.
You probably have ng-app="SSApp" somewhere in your HTML which is why Angular tries to bootstrap the module automatically.
You can choose to bootstrap the module manually by removing the ng-app directive and doing
angular.bootstrap(document.documentElement, ['SSApp']);
This is the change you have to do:
var myApp = angular.module("SSApp", []);
Init_Data(function () {
initApp();
});
function initApp() {
myApp.controller('config', function ($scope) {
});
console.log(myApp)
}
function Init_Data(callback) {
setTimeout(function () {
callback();
},4000);
}
From what I understand in your code, first you want to load data and then to add config controller to your app...so define your app first and then in your callback configure

AngularJS - Accessing ng-init variables from run method

1) I have variables initialized in ng-init
Eg -
ng-init="password='Mightybear'";
2) I want to access it from the .run method.
Eg -
anguar.module("ngApp", [])
.run(function() {
//Access password here
});
Below scenarios I have tried, and did not work -
1) angular.module("ngApp", [])
.run(function($rootScope) {
console.log($rootScope.password) //undefined!!!
});
2) angular.module("ngApp", [])
.run(function($rootScope, $timeout) {
$(timeout(function() {
console.log($rootScope.password) //undefined!!!
});
});
You can not get ng-init value inside your run block
Angular LifeCycle
Config Phase (app.config) ($rootScope will not available here)
Run Phase (app.run) ($rootScope will available here)
Directive Gets Compile()
Then controller, directive link function, filter, etc gets executed.(ng-init is here)
If you want to get initialize value in run phase then you need to set that value in config phase.
If You want to set the value in config then you can take use of app.constant/provider which available in configuration phase, don't use $rootScope that is considered as bad pattern in AngularJS.
Code
var app = angular.module('app', []);
app.constant('settings', {
title: 'My Title'
})
app.config(function(settings) {
setting.title = 'Changed My Title';
//here can you do other configurarion setting like route & init some variable
})
app.run(function(settings) {
console.log(settings.title);
})
I'll let you a walkthrough, of how angular loads
angular module ---> .config ----> .run ----> controllers(ng-init)
now you can clear your approach.

Angular Tests always fail on $logProvider when getting constants in tests

Very new to Angular testing... using 1.3.0.rc0. To get started I'm trying to do something simple: get the value of a constant I set. Within a config.js, I have the following:
(function () {
'use strict';
var app = angular.module('app');
// create app configuration
var appConfig = {
version = '0.0.1.0',
debugMode = true
};
app.constant('config', appConfig);
app.config([function ($logProvider, config) {
// set the debugging setting of the app > same setting for the app
if ($logProvider.debugEnabled) {
$logProvider.debugEnabled(config.debugMode);
}
}]);
})();
I'm tried numerous things to write my tests (using jasmine & karma), but I keep getting an error that:
Error: [$injector:modulerr] Failed to instantiate module app due to:
TypeError: 'undefined' is not an object (evaluating '$logProvider.debugEnabled').
I get that this was a bug a while ago in the angular-mocks.js file but has since been resolved. Regardless, no matter the test I write, it doesn't work. Here's what i'm working with now, knowing that there are issues with it.
'use strict';
describe('config.js', function () {
var logProvider;
beforeEach(module(inject(function ($log) {
logProvider = $log;
})));
beforeEach(module('app', logProvider));
it('should set the config constant to the app global configuration settings', function () {
var $injector = angular.injector(['ng', 'app']);
var settings = $injector.get('config');
//var settings = inject(config);
expect(settings.debugMode).toBe(true);
});
});
Am I doing this right? If so, is there no way to get around the test issue with $logProvider?
There is much to learn about how modules work in Angular, especially under testing with ngMocks. I'll try to be brief.
One always begins by calling module (from ngMocks) one (or more times) to build up the module "cookbook" for a test run.
In any of these module calls you have an opportunity to access and stash away a previously defined provider.
The first time you call inject (from ngMocks) in a given test path, the module "cookbook" is "baked" for that path and the injector is populated based on recipes in that "cookbook".
Subsequent calls to module are irrelevant. Your expression beforeEach(module('app', logProvider)); executes too late (even if it did what you wanted, which it would not).
In fact, I'm surprised that you didn't get the error: "Error: Injector already created, can not register a module!".
inject always returns the thing created by the provider, never the provider itself. Your first beforeEach ...
beforeEach(module(inject(function ($log) {
logProvider = $log;
})));
... actually sets logProvider to the $log service, not the $logProvider.
Does this help?
Here is a sample from my forthcoming course on Ng testing that shows how to access a provider (in this case, the $logProvider). It was inspired by your question.
First, the config2 constant (I already had a value called config:
// my sample application module definition is called 'basics'
var basics = angular.module('basics', []);
/* define 'config2' constant - which is available in Ng's config phase */
basics.constant('config2', {
debugMode: true
});
// use constant in config phase
basics.config(function ($logProvider, config2) {
$logProvider.debugEnabled(config2.debugMode);
})
Now the spec (using Mocha and Chai):
describe('Basics - constant:', function() {
'use strict';
beforeEach(module('basics'));
// other stuff
describe("the $logProvider", function(){
var configConstant;
var $log;
var $logProvider;
beforeEach(module(
// Could combine with module('basics') definition in outer describe
// but only need it here in this describe
// This module definition function has access to any previously defined provider
// which in this case is any provider defined in ng, ngMocks, or basics
function( _$logProvider_) {
$logProvider = _$logProvider_;
}
));
// inject triggers injector creation; module definition now "baked"
beforeEach(inject(function(config2, _$log_){
configConstant = config2;
$log = _$log_;
}));
it("is accessible via the module function", function(){
expect($logProvider).to.exist;
});
it("is not the same as the log service", function(){
expect($logProvider).not.to.equal($log);
});
it("has same debugEnabled value as config2.debugMode", function(){
expect($logProvider.debugEnabled()).to.equal(configConstant.debugMode);
});
});
});

Manually bootstrapping AngularJS and then getting the module

Generally, I'd do the following and there would be an ng-app in my HTML:
var myApp = angular.module("myApp", []);
myApp.controller("AttributeCtrl", function ($scope) {
$scope.master = {
name: "some name"
};
});
However, I need to manually bootstrap angular because I'm only using it in a part of my app that is loaded via jQuery's $.load(). If I do the following:
main.js - this is where the page I want to use angular on is being pulled in
$("#form").load(contextPath + url, params, function() {
angular.bootstrap($("#angularApp"));
});
And then the page being pulled in has it's own javascript:
function AttributeCtrl($scope) {
$scope.master = { name: "some name"};
}
This works, however, ideally, I'd like my controllers to be scoped at the module level. So I modified the above code like so
main.js
$("#form").load(contextPath + url, params, function() {
angular.bootstrap($("#angularApp", ["myApp"]));
});
and then...
var app = angular.module("myApp"); // retrieve a module
app.controller("AttributeCtrl", function($scope) {
$scope.master = { name: "some name"};
});
Retrieving the module this way doesn't seem to work, though. Am I doing something wrong?
You cannot create a controller after you've bootstrapped the app. See the documentation for angular.bootstrap.
You should call angular.bootstrap() after you've loaded or defined your modules. You cannot add controllers, services, directives, etc after an application bootstraps.
I don't know if this is just in the example code you have here but:
angular.bootstrap($("#angularApp", ["myApp"]));
should be
angular.bootstrap($("#angularApp"), ["myApp"]);
Your code for retrieving the module should work.
Updated
They updated the documentation and now it reads like this
Each item in the array should be the name of a predefined module or a
(DI annotated) function that will be invoked by the injector as a run
block. See: {#link angular.module modules}
It seems a bug.
The way you implemented to retrieve the module is correct. Just quote it from the doc to make it clear since it may not be well-known.
When passed two or more arguments, a new module is created. If passed
only one argument, an existing module (the name passed as the first
argument to module) is retrieved.
For the problem you mentioned, long story short...
The bootstrap function calls createInjector with the module list ['ng', ['ngLocale', function(){...}] , 'myApp'] (the last one is the module you passed in)
function bootstrap(element, modules) {
...
var injector = createInjector(modules);
Inside createInjector(), it calls loadModules for each module passed in
function createInjector(modulesToLoad) {
forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); });
And loadModules calls angularModule, which is initialized as angularModule = setupModuleLoader(window);, which creates the object window.angular.module
function loadModules(modulesToLoad){
....
var moduleFn = angularModule(module); // triggers the error
The the error occurs, since angularModule takes 2nd parameter as requires. Without it, it will throws an exception on this line (line 1148) throw Error('No module: ' + name);
Reported: https://github.com/angular/angular.js/issues/3692
Not sure if this counts as a bug or an implementation decision (albeit a seemingly poor one). Adding an empty array solves the undefined require problem that you were having and should solve your problem overall.
var app = angular.module("myApp", []); // create a module
app.controller("AttributeCtrl", function($scope) {
$scope.master = { name: "some name"};
});`
Also, in your fiddle you call {{name}} which won't render. You should be calling {{master.name}}
Edit
Thank you all for the downvotes .. Here's a working example. Good luck!
http://plnkr.co/edit/UowJpWYc1UDryLLlC3Be?p=preview

Resources