At some point this afternoon I suddenly got the following AngularJS error: Argument 'MainCtrl' is not a function, got undefined
I have a good version still running in Chrome without any errors, so I'm attempting to find the diffs - however to no avail.
I would appreciate any advice on how to fix the error Argument 'MainCtrl' is not a function, got undefined
I load the controllers.js file in index.html - within the body section:
<!DOCTYPE html>
<html ng-app="rage.ui">
<head>
<!-- loading css files here... -->
</head>
<body ng-controller="MainCtrl as main">
<!-- Wrapper-->
<div id="wrapper">
<!-- Navigation -->
</div>
<script src="app/app.js"></script>
<script src="app/config.js"></script>
<script src="app/js/controllers.js"></script>
</body>
Here's the MainCtrl in my controllers.js file (also loaded in index.html) -
* MainCtrl - controller */
(function () {
'use strict';
angular.module('rage.ui')
.controller('MainCtrl', ['$scope', '$interval', '$window', 'widgetDefinitions', main])
.factory('widgetDefinitions', [widgetDefinitions])
.value('defaultWidgets', [
{ name: 'random' },
{ name: 'time' },
{ name: 'datamodel' },
{
name: 'random',
style: {
width: '50%'
}
},
{
name: 'time',
style: {
width: '50%'
}
}
]);
function main($scope, $interval, $window, widgetDefinitions, defaultWidgets) {
this.userName = 'Risk user';
// ...
}
function widgetDefinitions(RandomDataModel) {
return [
{
name: 'random',
directive: 'wt-scope-watch',
attrs: {
value: 'randomValue'
}
}
{
name: 'treegrid',
templateUrl: 'app/views/templateGadget/treeGrid.html',
title: "Tree Grid Report",
attrs: {
width: '50%',
height: '250px'
}
}
];
}
})();
in my attempt to add a new 'gadgetModelFactory' to the malhar Angular-Dashboard framework, I broke my system.
Here's the good version of my new factory, now fixed:
'use strict';
angular.module('rage').
factory('gadgetDataModel', function ($scope, WidgetDataModel) {
function gadgetDataModel() {
var test = 123;
}
gadgetDataModel.prototype = Object.create(WidgetDataModel.prototype);
gadgetDataModel.prototype.constructor = WidgetDataModel;
angular.extend(gadgetDataModel.prototype = {
init: function () {
// to be overridden by subclasses
var dataModelOptions = this.dataModelOptions;
},
destroy: function () {
// to be overridden by subclasses
WidgetDataModel.prototype.destroy.call(this);
$interval.cancel(this.intervalPromise);
}
});
});
and my app.config:
(function () {
angular.module('rage.ui', [
'ui.router',
'ui.bootstrap',
'ui.dashboard'
])
})();
Have a look at other posts (AngularJS error: 'argument 'FirstCtrl' is not a function, got undefined')
Check whether you defined ng-app properly
Check whether you have listed the JS files properly in the index
Try to rewrite your controllers to the more conventional way (see egghead.io tutorials)
Maybe you have two files .js with same module name. Eg.:
file1.js:
var appControllers = angular.module('appControllers', []);
file2.js:
var appControllers = angular.module('appControllers', []);
Related
I want to load controllers on the fly when needed rather than loading them in one go. So, for the I've implemented dynamic approach which works fine without any error. It also works well with Ui-Router.
But the problem is in Index.html page. I want to put global (super parent) controller name "appCtrl". As this appCtrl should be initialized when I run my app. For that I need to write like ng-controller="appCtrl" or ng-controller="appCtrl as vm" at body tag.
But when I do it, it gives error that appCtrl is a function, got undefined. I tried sever ways but still unable to identify exact error. I have working on this issue since two to three days but still not able to identify it.
I have made this plunker.
look at body tag of index.html.
main.js
require.config({
paths: {
"angular": "//cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.0-rc.1/angular",
"ui-router": "//rawgit.com/angular-ui/ui-router/0.2.15/release/angular-ui-router"
},
shim: {
"angular": {
exports: 'angular'
},
"ui-router": {
deps: ['angular']
}
}
});
define(
['angular',
'app',
'controllers/appCtrl'],
function (angluar, app) {
angular.bootstrap(document, ['MyApp'])
});
app.js
define([
'angular',
'ui-router'
], function (angular) {
var app = angular.module('MyApp', ['ui.router']);
function lazy () {
var self = this;
this.resolve = function (controller) {
return {
ctrl: ['$q', function ($q) {
var defer = $q.defer();
require(['controllers/' + controller], function (ctrl) {
app.register.controller(controller, ctrl);
defer.resolve();
});
return defer.promise;
}]
};
};
this.$get = function (){
return self;
};
}
function config ($stateProvider, $urlRouterProvider,
$controllerProvider, $compileProvider,
$filterProvider, $provide, lazyProvider) {
$stateProvider
.state("home", {
url: "/",
controller: 'homeCtrl',
controllerAs: 'vm',
templateUrl: 'views/homeView.html',
resolve: lazyProvider.resolve('homeCtrl')
});
app.register = {
controller: $controllerProvider.register,
directive: $compileProvider.directive,
filter: $filterProvider.register,
factory: $provide.factory,
service: $provide.service,
constant: $provide.constant
};
}
app.provider('lazy', lazy);
app.config(config);
return app;
});
Index.html
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.17/require.js" data-main="main.js"></script>
</head>
<body ng-controller="appCtrl as vm"> // I want this to work correctly but it is not getting loaded dynamically. I don't know why. Help me to resolve it.
<a ui-sref="home">go home</a>
<ui-view></ui-view>
{{vm.appVar}}
</body>
</html>
You are actually missing the controller statement in your controller code.
Use the following code
define(['app'], function (app){ //Updated Line
console.log('app controller loaded');
app.controller('appCtrl',ctrl); //New Line added
ctrl.$inject = ['$http'];
function ctrl ($http) {
this.appVar = 'hi from appCtrl';
};
return ctrl;
});
See the plunker
I'm trying to use angular with requirejs. Here is my index.html file
<!DOCTYPE html>
<html>
<head>
<script data-main="/js/app.js" src="/bower_components/requirejs/require.js"></script>
</head>
<body >
<div ng-view ng-controller="accountController"></div>
</body>
</html>
app.js file
require.config (
{
appDir : '',
baseUrl : '/js/',
paths :
{
// Configure alias to full paths
'angular': '/bower_components/angular/angular.min',
'ngRoute': '/bower_components/angular-route/angular-route.min',
'main': 'main'
},
shim :
{
'main':{
deps: ["angular", 'ngRoute']
},
'ngRoute':{
deps: ["angular"]
}
}
});
require( [ "main" ], function( app )
{
// Application has bootstrapped and started...
});
main.js file
define([
'common/RouteManager'
], function(RouteManager){
var appName = 'elara',
depends = ['ngRoute'],
app = angular.module(appName, depends).config(RouteManager)
angular.bootstrap(document.getElementsByTagName('body')[0], [ ]);
return app;
});
RouterManager.js
(function ( define ) {
define([
'controllers/account'
], function(){
var RouteManager = $routeProvider.when("/", angularAMD.route({
templateUrl: '/html/tmpl/test.html', controller: 'registerController',
controllerUrl: 'controllers/account.js'
})).otherwise({redirectTo: "/"});
return ["$routeProvider", RouteManager ];
})
});
And finally my controller:
define(['common/settings'], function(settings){
var accountController = function($scope, $http){
$scope.username = "";
$scope.password = "";
$scope.register = function(){
$http.post(config.apiAddress + "account/login/", {username: $scope.username, password: $scope.password}).then(
function(data){
console.log(data);
}, function(response){
console.log(response);
})
}
};
return accountController;
});
I'm getting
Argument 'accountController' is not a function, got undefined
exception. When i check network tab, my account.js file is not loaded. So angular don't know anything about accountController
Brackets
You are returning a function in your controller so it should be
return accountController();
Not
return accountController;
I have following code:
JS:
var myApp = angular.module('app',['ngDropdowns', 'dataStoreService']);
myApp.controller('myCtrl', function($scope) {
$scope.ddSelectOptions = [
{
text: 'Option1',
iconCls: 'someicon'
},
{
text: 'Option2',
someprop: 'somevalue'
},
{
// Any option with divider set to true will be a divider
// in the menu and cannot be selected.
divider: true
},
{
// Example of an option with the 'href' property
text: 'Option4',
href: '#option4'
}
];
$scope.ddSelectSelected = {}; // Must be an object
});
and in DataStoreService.js:
var myApp = angular.module('app');
myApp.factory('dataStoreService', ['$rootScope', function ($rootScope) {
var service = {
model: {
name: '',
email: ''
},
SaveState: function () {
sessionStorage.userService = angular.toJson(service.model);
},
RestoreState: function () {
service.model = angular.fromJson(sessionStorage.userService);
}
}
$rootScope.$on("savestate", service.SaveState);
$rootScope.$on("restorestate", service.RestoreState);
return service;
}]);
But, I am getting the following error:
https://docs.angularjs.org/error/$injector/nomod?p0=dataStoreService - Module 'dataStoreService' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
How do I resolve it? How can i link different controllers and services in different files to my app?
app.js
// Declare the main module
var myApp = angular.module('myApp', []);
myApp.controller('Ctrl1', ['$scope', 'MyService', '$window', function($scope, MyService, $window) {
var str = MyService.getHello();
$window.alert(str);
}]);
aService.js
angular.module('myApp').
service('MyService', function() {
return {
getHello: function () {
return 'hello';
},
}
}
);
index.html
<!DOCTYPE html>
<html ng-app="myApp">
<head>
</head>
<body>
<p>An example showing how to share data between controllers using a service</p>
<div ng-controller="Ctrl1">
<h2>Controller 1 Says:</h2>
</div>
<script src="http://code.angularjs.org/1.2.1/angular.js"></script>
<script src="app.js"></script>
<script src="aService.js"></script>
</body>
</html>
inject it in the controller rather than application:
var myApp = angular.module('app',['ngDropdowns']);
myApp.controller('myCtrl', ['$scope', 'dataStoreService', function($scope, dataStoreService) { ....
here,
var myApp = angular.module('app',['ngDropdowns']);
you don't need the 'dataStoreService' define as a dependency here, its not a module its just a service, so if you need that service inside controller inject it into the controller like below,
myApp.controller('myCtrl', function($scope,dataStoreService) { ....
I've made a module code structure, here is my index.html:
<!doctype html>
<html>
<head>
<!-- AngularJS -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.18/angular.min.js"></script>
<script src="https://code.angularjs.org/1.3.0-beta.18/angular-route.min.js"></script>
<!-- Lazy loading module -->
<script src="assets/libs/ocLazyLoading.min.js"></script>
<!-- App -->
<script src="app/app.js"></script>
<script src="app/config.js"></script>
<script src="app/article/article.js"></script>
<base href="http://localhost/angularjs-blog/">
</head>
<body data-ng-app="Blog">
<div data-ng-view></div>
</body>
</html>
app.js:
var app = {};
app.main = angular.module('Blog', [
// Tools
'ngRoute',
'oc.lazyLoad',
// Modules
'Blog.Article'
]);
config.js:
app.main.config(['$routeProvider', function ($routeProvider) {
$routeProvider.otherwise({
redirectTo: '/'
});
}]);
article.js:
app.article = angular.module('Blog.Article', ['ngRoute', 'oc.lazyLoad']);
app.article.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'app/article/views/articles.html',
controller: 'ArticlesController',
resolve: {
lazy: ['$ocLazyLoad', function($ocLazyLoad) {
return $ocLazyLoad.load({
name: 'Blog',
files: [
'app/article/controllers/articles.controller.js',
'app/article/article.service.js',
'common/directives/button.directive.js'
]
});
}]
}
})
.when('/article/:id', {
templateUrl: 'app/article/views/article.html',
controller: 'ArticleController',
resolve: {
lazy: ['$ocLazyLoad', function($ocLazyLoad) {
return $ocLazyLoad.load({
name: 'Blog',
files: [
'app/article/controllers/article.controller.js',
'app/article/article.service.js'
]
});
}]
}
});
}]);
article.controller.js:
// Controller
app.article.controller('ArticleController', ['$scope', 'ArticleService', function ($scope, ArticleService) {
}]);
This is the problem, I can't write app.article.controller as my router does not find "ArticleController". I have to write app.main.controller instead. And it seems weird for me. I've created an object property for this module, and I don't want to see the controller in the main property, right?
I really don't understand what's going on. I'm using AngularJS version 1.3.0-beta.18.
ANSWER: As I my button.directive.js was in app.main, the right routing is:
app.article = angular.module('Blog.Article', ['ngRoute', 'oc.lazyLoad']);
app.article.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'app/article/views/articles.html',
controller: 'ArticlesController',
resolve: {
lazy: ['$ocLazyLoad', function($ocLazyLoad) {
return $ocLazyLoad.load([{
name: 'Blog',
files: ['common/directives/button.directive.js']
}, {
name: 'Blog.Article',
files: [
'app/article/controllers/articles.controller.js',
'app/article/article.service.js'
]
}]);
}]
}
})
.when('/article/:id', {
templateUrl: 'app/article/views/article.html',
controller: 'ArticleController',
resolve: {
lazy: ['$ocLazyLoad', function($ocLazyLoad) {
return $ocLazyLoad.load({
name: 'Blog.Article',
files: [
'app/article/controllers/article.controller.js',
'app/article/article.service.js'
]
});
}]
}
});
}]);
from documentation
controller – {(string|function()=} – Controller fn that should be associated with newly created scope or the name of a registered controller if passed as a string.
when you are writing ArticleController angular don't know in which namespace to look, because your controller stored in other module therefore you must provide the right reference (namespace) for your controller
UPDATE 1:
the problem was that what you embedded the angular lib after body and your script (jsffidlle script) worked before angular had been attached let me update example
This is an updated example
Example... Plunker
addon.js:
(function () {
'use strict';
angular.module('jzAddons', [])
.factory('jzThemeFac', function () {
return {
themes:[
{
name: 'none',
url: '',
margin: '8px',
bg: '#ffffff'
},
{
name: 'amelia',
url: '//netdna.bootstrapcdn.com/bootswatch/3.1.0/amelia/bootstrap.min.css',
margin: '6px',
bg: '#108a93'
}
],
changeTheme: function (theme) {
var oldLink = angular.element('#bootstrapTheme');
oldLink.remove();
if (theme.name !== 'none') {
var head = angular.element('head');
head.append('<link id="bootstrapTheme" href="' + theme.url + '" rel="stylesheet" media="screen">');
}
currentTheme = theme;
}
};
});
})();
app.js:
var app = angular.module('example', ['jzAddons']);
(function () {
'use strict';
app.controller('MainCtrl', ['jzThemeFac', function ($scope, jzThemeFac) {
$scope.themes = jzThemeFac.themes; /* Error comes from this line */
}]);
})();
At the bottom of my body tag I've got:
<script src="addon.js"></script>
<script src="app.js"></script>
I keep getting the error:
TypeError: Cannot read property 'themes' of undefined
Your factory inject was wrong, you can do:
app.controller('MainCtrl', ['$scope', 'jzThemeFac', function ($scope, jzThemeFac) {...
or just:
app.controller('MainCtrl', function ($scope, jzThemeFac) {
edit: you can also move:
(function () {
'use strict';
to the beginning of file
You need to remember to inject $scope into your MainCtrl. So app.js should look like this:
var app = angular.module('example', ['jzAddons']);
(function () {
'use strict';
app.controller('MainCtrl', ['$scope', 'jzThemeFac', function ($scope, jzThemeFac) {
$scope.themes = jzThemeFac.themes; /* Error comes from this line */
}]);
})();