Angular Controller Instantiated Multiple Times - angularjs

I have an Angular controller loaded in a view:
<div ng-controller="MyCtrl">
<p ng-bind-html="content"></p>
</div>
This partial is loaded into different views, and as a result the controller gets instantiated multiple times. In the controller, I'm detecting for location change:
angular.module('MyApp')
.controller('HintCtrl', function ($scope, $rootScope) {
$rootScope.$on('$locationChangeSuccess', function () {
alert("HI");
});
});
Each time I change my location, this fires once for each time the controller was ever loaded. How can I have this run only once?

The point is that Controllers are not singleton. You gonna have one new instance for each of the elements.
What you can do, is to use this on a service, and this one is singleton.
You could do something like this:
angular.module('MyApp')
.controller('HintCtrl', function ($scope, Alerter) {
Alerter.doSomething();
})
.service('Alerter', function($rootScope) {
$rootScope.$on('$locationChangeSuccess', function () {
alert("HI");
});
this.doSomething = function() {
...
};
});

Related

AngularJS: inject dependency into every controller

Suppose that I have a huge web application (that uses AngularJS) with a lot of controllers. Is there a way to inject $log service in every controller? To be more clear, I want to write something like this:
.config(function($log) {
allMyControllers.inject($log);
})
instead of
.controller('Controller1', function($log) {...})
.controller('Controller2', function($log) {...})
.controller('Controller3', function($log) {...})
.controller('Controller4', function($log) {...})
Possible thing that you can do is, create a controller that has all needed dependencies and make it as base controller and other controllers can extend it using angular extend api.
some clear example code which I came accross :
.controller('baseController', function(someService) {
this.someService = someService;
})
.controller('extendedController', function($scope, $controller) {
angular.extend(this, $controller('baseController', { $scope: $scope }));
this.alert = this.someService.alert;
})
.service('someService', function() {
this.alert = function() {
window.alert('alert some service');
};
});
Working solution of above code can be found here.

Call a Controller angularjs in another Controller angularjs

Well my problem is I have 2 controllers... and I wanna know whether is possible call any function from them in order to do work them together...
For instance:
firstController.js
angular.module('myApp').controller('FirstController', ['$scope', 'FirstService', function($scope, FirstService) {
function verify(){
//Some operations
}
}]);
secondController.js
angular.module('myApp').controller('SecondController', ['$scope', 'SecondService', function($scope, SecondService) {
function edit(iditem){
//Some operations
//I wanna call here verify() function
}
}]);
Is this possible?
Thanks in advance!
You could do it with only one service and call service from both controllers. This is a basic example, you could add a constructor and pass it $scope for example it depends on what you want to accomplish.
'use strict';
/**
* Service
*/
angular.module('myApp.someService', [])
.factory("someService", function () {
return {
verify: function() {},
edit: function(iditem){
self.verify();
}
});
Then your controllers would look like this:
angular.module('myApp').controller('FirstController', ['$scope', 'someService', function($scope, someService) {
service.verify($scope);
}]);
angular.module('myApp').controller('SecondController', ['$scope', 'someService', function($scope, someService) {
service.edit(iditem);
}]);
Using Prototypical Inheritance
If the controllers are nested:
<div ng-controller="FirstController">
<div ng-controller="SecondController">
</div>
</div>
If the FirstController publishes its functions on $scope, the nested controllers can invoke the functions by prototypical inheritance.
Publish function on $scope:
app.controller('FirstController', function($scope) {
$scope.firstVerify = verify;
function verify(){
//Some operations
}
});
Invoke by $scope inheritance:
app.controller('SecondController', function($scope) {
function edit(iditem){
//Some operations
//I wanna call here verify() function
$scope.firstVerify();
}
});
This will only work if the second controller's scope is a child scope of the first controller's scope.
As you can see, there are many answers to the question. Which answer to use depends on the specific use case.
You can do that through $rootScope controller
include $rootScope in FirstContoller, assign a function:
$rootScope.myFirstController_verify = verify;
And call it from second controller with $rootScope included:
$rootScope.myFirstController_verify();

Cannot Access parent controller's method from the child UI

I have a popup form where it's having lot of tabs.
Its' like this :
index.js
vm.openCreateOrEditPropertyModal = function (resolveProperty) {
var modalInstance = $uibModal.open({
templateUrl: '~/App/tenant/views/propertymanagement/createOrEditPropertyModal.cshtml',
controller: 'tenant.views.propertymanagement.createOrEditPropertyModal as vm',
resolve: {
resolveProperty: function () {
return resolveProperty;
}
}
});
};
Tabs are like this :
createOrEditPropertyModal.cshtml
<uib-tabset class="tab-container tabbable-line" type="pills">
<uib-tab heading="#L("PropertyInformation")">
<div ng-include="'~/App/tenant/views/propertymanagement/tabs/propertyForm.cshtml'"></div>
</uib-tab>
</uib-tabset>
createOrEditPropertyModal.js
(function () {
appModule.controller("tenant.views.propertymanagement.createOrEditPropertyModal", [
"$scope", "resolveProperty", "localStorageService", "$uibModalInstance", function ($scope, resolveProperty, localStorageService, $uibModalInstance) {
var vm = this;
//to close the pop up
vm.cancel = function () {
$uibModalInstance.close();
};
}
]);
})();
propertyForm.cshtml
<div ng-controller="tenant.views.propertymanagement.tabs.propertyForm as vm">
<button type="button" ng-click="vm.cancel()">#L("Close")</button>
</div>
propertyForm.js
(function () {
appModule.controller("tenant.views.propertymanagement.propertyForm", [
"$scope", "resolveProperty", "localStorageService", function ($scope, resolveProperty, localStorageService) {
var vm = this;
}
]);
})();
Above set up is working fine.Now I need to access cancel() method on the createOrEditPropertyModal.js file from the child form (i.e. propertyForm.cshtml).But it's not being fired.Can you tell me how to do that ? I have tried like this ng-click="$parent.cancel()".But it's not working.
You can do vm.cancel() directly inside a child form as you are already using controllerAs pattern. But in your case you have both controller alias as vm it is referring to current child controller context. Changing name of either of controller alias would make it working.
Best way to solve this issue would change your controller alias to different name. Controller aaliases should be like by looking at their name's you can pretend which controller it is.
--
The ng-click isn't working because cancel method isn't there inside $parent scope. You should be doing $parent.vm.cancel(). But I'd not prefer you to use $parent on view, because bad practice.
I can think of three possible solutions:
It's because the cancel function is not on the $parent scope, but on the parent controller. One way to achieve this is probably to use different naming for controllers: related SO answer
Otherwise, you could put the cancel function on the $scope variable in createOrEditPropertyModal.
Just change createOrEditPropertyModal.js:
vm.cancel = function () {
$uibModalInstance.close();
};
to
$scope.cancel = function () {
$uibModalInstance.close();
};
You can pass functions to modals similar to directives: related SO answer

Angularjs: Injecting one controller in another failed

I have this controller A which I'm trying to inject in every other controller.
What controller A does is, it communicates with a factory (which does some authentication services, communicates with database)
My factory looks like this and I named it myFactoryServices.js and included the link to it in my index page.
(function() {
angular
.module('myApp.myFactoryServices', [])
.factory('FactoryService', ["$http", "$location", function($http, $location){
var my = this;
my.someFunction = function()
{
//communiate with backend and return data
}
return my;
}]);
})();
and my Controller A looks like this:
(function() {
angular
.module('myApp.ControlA', [])
.controller('ControllerA', function($scope,$routeParams, FactoryService) {
var my = this;
FactoryService.someFunction();
});
})();
And I am trying to inject this controller in every other controller, but it does not work. I am pretty new to this kind of programming, can anyone tell me where I made mistake?
This is how I tried injecting a controller into another.
(function() {
angular
.module('myApp.ControlB', [])
.factory('ControllerBService', function($http) {
var baseUrl = 'backendurl/';
return {
getInfo: function() {
return $http.get(baseUrl+ 'getInfo');
}
};
})
.controller('ControllerB', function($scope,$routeParams, ControllerBService,ControllerA) {
var my = this;
});
})();
No error is coming, and the controller is not getting injected as I am not able to use those factory services. is this the correct method?
First of all you cannot inject controller to another controller, and One simple solution would be, instead of having each angular modules for each components, declare a module and add the factory service to controllers as dependency.
Code:
var app = angular.module('myApp', []);
app.factory('FactoryService', ["$http", "$location", function($http, $location){
var my = this;
my.someFunction = function()
{
//communiate with backend and return data
}
return my;
}]);
app.controller('ControllerA', function($scope,$routeParams, FactoryService)
{
var my = this;
FactoryService.someFunction();
});
app.controller('ControllerB', function($scope,$routeParams, FactoryService)
{
var my = this;
FactoryService.someFunction();
});
Controllers are not injectable, because controller is not singleton. Controllers are constructor functions used to create instances of controllers. For example you can have multiple instances of one controller in your app:
angular.module('app', []);
angular
.module('app')
.controller('Example', function () {
this.x = Math.random();
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.js"></script>
<div ng-app="app">
First instance: <br>
<div ng-controller="Example as Ex1">
{{Ex1.x}}
</div>
Second instance: <br>
<div ng-controller="Example as Ex2">
{{Ex2.x}}
</div>
</div>
So if you want to share some behavior between controller you should use factory instead.
To inject a controller into another controller, use the $controller service.
app.controller('ControllerB', function($scope,$routeParams, $controller) {
var my = this;
$scope.ctrlA = $controller("ControllerA", {$scope: $scope});
});
The above example creates an instance of ControllerA as ctrlA with its $scope injected with the scope of the parent controller.
For more information, see AngularJS $controller Service API Reference.

Is possibile to configure a controller (like services) in AngularJS?

I have my CartDropdownController that can be configured (i.e. setting autoCloseDelay) and I'm facing the following problem: how to inject the controller in angular.run() method?
angular.module('app')
.controller('CartDropdownController', ['$scope', function ($scope) {
this.autoCloseDelay = 3000;
this.setAutoCloseDelay = function (autoCloseDelay ) {
this.autoCloseDelay = autoCloseDelay;
};
// $scope variables here...
}]);
I know that services can be injected in .run() (like $http), but my controller is not a service (and I don't know how to make it a service...).
Edit: why? I don't want my controller to depend on values/constants:
angular.module('app')
.value('config', {
cart: {autoCloseDelay: 1500}
})
.controller('CartDropdownController', [
'$scope',
'config',
function ($scope, config) {
var autoCloseDelay = config.cart.autoCloseDelay || 3000;
// ...
}
]);
docs say You can only inject instances (not Providers), and yes controller itself is also an instance, just like a service.
Now See this example:
var myApp = angular.module('myApp', []);
myApp.factory('aProvider', function() {
console.log("factory invoked");
return{
fun:function(from){
console.log("factory from"+from);
}
}
});
myApp.directive("test1", function() {
console.log("directive setup");
return {
compile: function() {console.log("directive compile");}
}
});
myApp.directive("test2", function() {
return {
link: function() {console.log("directive link");}
}
});
myApp.run(function(aProvider) {
console.log("app run");
// aProvider.fun('from run');
});
myApp.config( function() {
console.log("app config");
});
myApp.controller('myCtrl', function($scope) {
console.log("app controller");
});
And the result:
app config
(index):24 factory invoked
(index):46 app run
(index):33 directive setup
(index):35 directive compile
(index):56 app controller
(index):41 directive link
What happens here:
config block executed
factory is invoked(instances, services or factories)
run executed
directives set
controllers initialized
directives linked to the page
Now here, you see run is executed first and controllers are initialized afterwords. This is why controllers cant be injected into app.run()

Resources