function called twice inside angularjs controller - angularjs

I am new to angular js and currently stuck with very wired kind of a bug. function in a controllers runs twice when its called by view loaded against a route.
http://jsfiddle.net/4gwG3/5/
you will see alert twice!!
my view is simple
and my app code is following
var IB = angular.module('IB', []);
//channel controller
IB.controller('channelsController', function ($scope, $routeParams) {
$scope.greet = function () {
alert('hi');
};
});
IB.config(function ($routeProvider) {
$routeProvider
.when('/channels', {
controller: 'channelsController',
template: '{{greet()}}'
})
.otherwise({ redirectTo: '/channels' });
});

First check that you're not initializing your Angular app twice (by having it initialized automatically with ng-app).
One time I had 2 html pages with ng-app (one for login.html and
another for main.html) and this was a problem I realized later.
Second and for me the most important, check if you have attached your controller to multiple elements. This is a common case if you are using routing.
In my case I was navigating to DashboardController like so:
app.config(function($routeProvider){
$routeProvider
.when('/', {
controller: 'DashboardController',
templateUrl: 'pages/dashboard.html'
})
});
But I also had this in dashboard.html:
<section class="content" ng-controller="DashboardController">
Which was instructing AngularJS to digest my controller twice.
To solve it you have two ways:
removing ng-controller from your html file like this:
<section class="content">
or removing controller from routing (that is normally situated in app.js):
app.config(function($routeProvider){
$routeProvider
.when('/', {
templateUrl: 'pages/dashboard.html'
})
});

I think by creating an interpolation {{greet()}}, you create a watch on function greet. This function can get call as many time as digest cycle runs, so it is not a question about it running 1 or 2 times. So you should not depend upon the times the function is called.

I dont know what you are trying to achieve here. There are two alerts
1. When the controller is called.
2. When the template is get evaluated.
template is to provide the view part, however, in this case template is just evaluating function which is not creating any view.

I had the same problem, so I did:
$scope.init=function()
{
if ($rootScope.shopInit==true) return;
$rootScope.shopInit=true;
...
}
$scope.init();
Like if it were a singleton ! (I had many ajax calls each time I display, it was boring)

Related

Argument 'mainController' is not a function, got undefined 1.4

There are a ton of examples of using the newer angular directives like ng-blur, ng-focus, form validation, etc. They all work great in a single page, or in plinkr, jsfiddle, etc. with the exception of the people who try to define the function on the global namespace, that mistake is WELL documented.
However, I was having a different problem.
I was using an example from Scotch.io. This one works great...until you introduce it into an SPA that is using angular-route :(
After many hours of fighting with the error 'Argument 'mainController' is not a function, got undefined', I found the answer in a comment from Hajder Rabiee.Thanks Hadjer, Love you man!
Hajder left this comment and in it, he says:
If you're using routes (high probability) and your config has a reference to a controller in a module that's not declared as dependency then initialisation might fail too.
E.g assuming you've configured ngRoute for your app, like
angular.module('yourModule',['ngRoute'])
.config(function($routeProvider, $httpProvider) { ... });
Be careful in the block that declares the routes,
.when('/resourcePath', {
templateUrl: 'resource.html',
controller: 'secondModuleController' //lives in secondModule
});
Declare secondModule as a dependency after 'ngRoute' should resolve the issue. I know I had this problem.
Even with this help it took me a minute to get it working, so I thought I would share my sample code here to help the next poor bastard that gets stuck on this.
First, in the place where i declare my routes:
var app = angular.module('sporkApp', ['ngRoute','validationApp']);
app.config(function ($routeProvider) {
$routeProvider
.when('/home',
{
controller: 'HomeController',
templateUrl: 'home/home.template.html'
})
.when('/tags',
{
controller: 'TagsController',
templateUrl: 'tags/tags.template.html'
})
.when('/test',
{
controller: 'mainController',
templateUrl: 'test/test.template.html'
})
.otherwise({ redirectTo: '/home' });
});
Then, you need to add your controller code somewhere, where it will get loaded in your shell page:
// create angular app
var validationApp = angular.module('validationApp', []);
// create angular controller
validationApp.controller('mainController', function($scope) {
// function to submit the form after all validation has occurred
$scope.submitForm = function() {
// check to make sure the form is completely valid
if ($scope.userForm.$valid) {
alert('our form is amazing');
}
};
});
Finally, you need to add the corresponding ng-app and ng-controller to some page element that wraps the controls you want to validate. I put the following inside of a div tag:
<div ng-app="validationApp" ng-controller="mainController">

Why doesn't $location.path fire the $routeProvider function?

I'm trying to build my first AngularJS single page application. I copied part of the code below and modified it some. I have a menu bar which calls the Navigate method that I added to the mainController.
When I click on the menu button the expected alert message appears in Navigate, but the alert message in the $routeProvider function only fires when the application starts and it never fires again. I can't find a good explanation of this, but logic says the $routeProvider function should fire when a new $location.path is set in Navigate. Is that wrong? How is this supposed to wire up? Is my nested single page controller causing the menu command to fail?
Also, are there really supposed to be two semicolons at the end or should one of them come after the app.config section?
var app = angular.module('myApp', ['ngRoute']);
app.config(['$routeProvider', function ($routeProvider)
{
alert("$routeProvider " + $routeProvider.path);
$routeProvider
.when('/', {
templateUrl: 'App/Views/Home.html',
controller: 'homeController'
})
.when('/about', {
templateUrl: 'App/Views/About.html',
controller: 'aboutController'
})
.otherwise({
redirectTo: '/'
});
}])
app.controller('mainController', function ($scope)
{
$scope.Title = "Default Title";
$scope.Message = "Default Message";
$scope.Navigate = function (myPath)
{
alert("Navigate " + myPath);
$location.path(myPath);
};
});;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<ul class="MenuBar">
<li class="MenuButton FloatLeft" ng-click="Navigate('/home');">Home</li>
<li class="MenuButton FloatLeft" ng-click="Navigate('/about');">About</li>
<li class="MenuButton FloatLeft" ng-click="Navigate('/about');">Log In</li>
</ul>
You're trying to run an alert whenever the $routeProvider function runs but it doesn't quite work that way. The $routeProvider function just tells Angular "Whenever the location path changes, refer to this JSON object to know what to do next." Then your code providers some JSON attributes to Angular such as templateUrl and controller. Your alert function will only run once because the $routeProvider is just setup code to configure Angular's routes.
To run code after going to another "page", just add the code to the controller.
Code Example:
app.controller('homeController', function($scope, $http) {
alert("I'm running the homeController() function now");
});
Also, I noticed that you didn't inject $location into your controller. Without this, $location will just be an undefined object. Change your controller definition like this:
app.controller('mainController', function ($scope, $location)
{
$scope.Title = "Default Title";
$scope.Message = "Default Message";
$scope.Navigate = function (myPath)
{
alert("Navigate " + myPath);
$location.path(myPath);
};
});
Remember that any Angular object starting with the $ dollar sign ($timeout, $http, $routeProvider, etc) must be injected into your controller.
You have a few issues with the code you're showing. Not enough details to know for sure but here's what's wrong.
First:
A module's config block will only be executed once, at the start. You're not seeing the alert within your config beyond once because it's only ever called once during the bootstrap of your module.
Second:
You need to inject services that your controller depends on.
app.controller('mainController', function ($scope) { });
Note that you're missing the $location service here.
app.controller('mainController', function ($scope, $location) { });
Third:
We can't see some missing pieces to your code to help you out. You're not showing us how mainController is actually hooked up to anything. How myPath is being sent to the Navigation function on your controller, etc.
I found a nested controller that I wasn't using. When I took that out part of the menu worked. I say part because on some links instead of calling the Navigate function I was setting the window.location. That seems to fire the $routeProvider and the view changes like it should. When I change the Navigate function as shown below it works. I think setting $location.path() in the Navigate function should do the same thing, but it's not working for me.
$scope.Navigate = function (myPath)
{
alert("Navigate " + myPath);
//$location.path(myPath);
window.location = '#' + myPath;
};

IONIC Framework - template is getting reloaded 10-12 times & so the controller functions

I have verified, controller is getting loaded only once not multiple times. Also, If I add more routes in app.js the number of print keeps increasing as well.
Issue demo: http://codepen.io/anon/pen/XXxxpM (check console in browser)
.controller('AuthSignUpCtrl', function($scope, $state) {
var i=01;
$scope.test = function() {
console.log(i++);
};
});
$stateProvider
.state('login', {
url: '/login1',
templateUrl: 'templates/login.html',
controller: 'AuthSignUpCtrl'
});
I don't think your template is being reloaded.
I think its because you bound your function to an expression.
Try typing into the text box, this will cause the digest loop to cycle and will fire your function, resulting in more output in the console.

$routeProvider not firing Controller

I have a situation where the Angular $routeProvider appears to not fire controller actions on route changes.
The routes are super simple urls:
window.app = angular.module('app', ['ngRoute', 'app.filters', 'app.services', 'app.directives', 'app.controllers'])
.config([
'$routeProvider', function($routeProvider) {
console.log("app.js config launched");
$routeProvider
.when('/nav', {
templateUrl: 'temp/test.html',
controller: 'navController'
// controller: function($scope) { alert('scope called.') }
})
.when('/home', {
controller: 'homeController',
template: ' '
});
$routeProvider.otherwise({ redirectTo: '/home' });
}
]);
The controller is just an log out to verify access:
app.controller('navController', [
"$scope", "cellService",
function ($scope, cellService) {
console.log("**** navController fired");
}
]);
The initialization code fires so the routing is initialized. When I hit:
http://localhost:4333/app/#/nav
and the url changes I can see that the test.html template is accessed by the browser, but the controller never fires.
This seems to indicate the route is getting activated by the URL change, but for some reason the controller is not firing. I've tried using a function instead of a controller name, but that too never gets fired. I've also verified that the controller is valid by attaching ng-controller="navController" to an element and that fires the controller just fine.
This is a page that originally didn't have routing associated as it was basically single self-contained page that didn't need navigation. I added the route code after the fact. I added an ng-view (there wasn't one before) after which at least the template started loading - without ng-view nothing happens.
Stumped and not sure what else to look at. Help.
It turns out the problem really was operator error on my part, but I think it's a scenario that can cause issues so I'll use this as the answer.
The issue that caused this problem were two-fold:
The HTML template HTML page (via templateUrl) had an invalid URL so the page never loaded
and the controller wasn't fired because of that.
When switching to a template I used an empty template (" ") but had also
removed the ng-View directive. The ng-View directive MUST BE present
even when using an empty template. Without it the controller doesn't fire.
In both cases it didn't work and I mistakenly assumed that the controller was not getting fired which was confusing because it did fire if I explicitly hooked it up with ng-controller.
Yup plain operator error, but the latter is vitally important - without ng-View the controller doesn't fire.
What happens if you define the function externally and reference that? So instead of
.when('/nav', {
templateUrl: 'temp/test.html',
controller: 'navController'
})
It would be
.when('/nav', {
templateUrl: 'temp/test.html',
controller: navController
})
and elsewhere
function navController($scope, cellService){
console.log("**** navController fired");
}
navController.$inject = ['$scope', 'cellService'];

Angular $routeParams is blank

I have a really simple Angular app that I've distilled to the following:
var napp = angular.module('Napp',['ngResource']);
var CompanyCtrl = function($scope, $routeParams, $location, $resource) {
console.log($routeParams);
};
napp.config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/company/edit/:id',
{templateUrl: '/partials/edit', controller: 'CompanyCtrl'}
);
}]);
and the HTML:
<div ng-controller="CompanyCtrl"></div>
When I log $routeParams, it comes up blank. When I use .otherwise(), it will load whatever I've specified there. Any idea what I'm missing?
You have a couple of errors:
You've specified the controller in two places, both in the view (<div ng-controller="CompanyCtrl"></div>) and in $routeProvider (.when('/company/edit/:id', {templateUrl: '/partials/edit', controller: 'CompanyCtrl'}). I'd remove the one in the view.
You have to register the controller in the module when specifying it in the $routeProvider (you should really do this anyway, it's better to avoid global controllers). Do napp.controller('CompanyCtrl', function ... instead of var CompanyCtrl = function ....
You need to specify a ng-view when you're using the $route service (not sure if you're doing this or not)
The new code:
var napp = angular.module('Napp', ['ngResource']);
napp.controller('CompanyCtrl', function ($scope, $routeParams, $location, $resource) {
console.log($routeParams);
});
napp.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/company/edit/:id',
{templateUrl: '/partials/edit', controller: 'CompanyCtrl'}
);
}]);
The template (/parials/edit)
<div> ... </div>
And the app (index.html or something)
... <body> <div ng-view></div> </body>
I've created a working plunker example: http://plnkr.co/edit/PQXke2d1IEJfh2BKNE23?p=preview
First of all try this with
$locationProvider.html5Mode(true);
That should fix your starting code. Then adjust your code to support non-pushState browsers.
Hope this helps!
Not sure if this helps, but I just came across this issue myself, and found that I couldn't log the route params until I had something bound to them.
So,
Router:
var myApp = angular.module('myApp', []);
myApp.config(function($routeProvider){
$routeProvider.when('/projects/:id',
{templateUrl: '/views/projects/show.html', controller: 'ProjectCtrl'}
);
});
Controller:
myApp.controller('ProjectCtrl', function($scope, $routeParams){
$scope.id = $routeParams.id;
console.log('test');
});
View:
<h1>{{ id }}</h1>
When I removed the '{{id}}' from the view, nothing was logged and $routeParams was empty, at least at the time of the controller's instantiation. As some of the answers above have pointed to, the route params are passed in asynchronously, so a controller with no bindings to that property won't execute. So, not sure exactly what you've distilled your snippet down from, but hope this helps!
This may happen (not in the OP's case) if you're using ui-router instead of ngRoute.
If that's the case, use $stateParams instead of $routeParams.
https://stackoverflow.com/a/26946824/995229
Of course it will be blank. RouteParams is loaded asynchronously so you need to wait for it to get the params. Put this in your controller:
$scope.$on('$routeChangeSuccess', function() {
console.log($routeParams);
});
It works for me http://plunker.co/edit/ziLG1cZg8D8cYoiDcWRg?p=preview
But you have some errors in your code:
Your don't seem to have a ngView in your code. The $routeProvider uses the ngView to know where it should insert the template's content. So you need it somewhere in your page.
You're specifying your CompanyCtrl in two places. You should specify it either in the $routeProvider, or in you template using ng-controller. I like specifying it in the template, but that's just personal preference.
Although not an error, you're specifying your CompanyCtrl in the global scope, instead of registering it on your Napp module using Napp.controller(name, fn).
Hope this helps!
You can always go on #angularjs irc channel on freenode: there's always active people ready to help
Could it be that your templateUrl points to an invalid template?
When you change the templateUrl to an unexisting file, you will notice that the $routeParams will no longer be populated (because AngularJS detects an error when resolving the template).
I have created a working plnkr with your code for your convenience that you can just copy and paste to get your application working:
http://plnkr.co/edit/Yabp4c9zmDGQsUOa2epZ?p=preview
As soon as you click the link in the example, you will see the router in action.
Hope that helps!

Resources