I have a function defined in a controller , I want to call it in another controller.
I tried to attach it to the $rootscope so I can see in the other controller , but I couldn't .
Is there a way for calling it, without attaching it to the $rootscope?
As far as I know in AngularJS you can share info between controllers in 3 ways:
1 - Creating a Service.
2 - Creating a function linked to $rootScope.
3 - Using events ($broadcast and $on). I use a lot this method in my projects.
I think your problem is that you don't instantiate the controllers in
the proper order or one of them is never instantiated, therefore the
function you want to link to $rootScope in that controller or the broadcast event never fires.
E.G If you want to call a function linked to $rootScope in the 2 controller from the
first one, it is impossible because the 2 controller is instantiated after the first one.
This case happens when you make calls on application runtime.
I will implement your method with some changes:
HTML:
<div ng-controller="MyCtrl_1"></div>
<div ng-controller="MyCtrl_2">
<button ng-click="send()">Send Mess</button>
</div>
JS:
var myApp = angular.module('myApp',[]);
function MyCtrl_1($scope, $rootScope) {
$scope.$on('RenderPage', function (event, PageId) {
$scope.RenderPage = PageId;
alert($scope.RenderPage);
});
};
function MyCtrl_2($scope, $rootScope) {
$scope.MasterPageId = 10;
$scope.send = function(){
$rootScope.$broadcast('RenderPage', $scope.MasterPageId);
}
};
Use carefully $broadcast and $emit, because has different behavior each one.
Try here: http://jsfiddle.net/1ypkb4s9/
Otherwise, post your error.
Simply wrap them with a "father controller":
HTML:
<div ng-app="myApp" ng-controller="myOuterCtrl">
<div ng-controller="myInnerCtrl1">
<button ng-click="outerClick()">Outer Click</button>
</div>
<div ng-controller="myInnerCtrl2">
<button ng-click="innerTwoClick()">Inner Click</button>
</div>
</div>
JS:
angular.module('myApp', []).
controller('myOuterCtrl', function ($scope) {
$scope.outerClick = function () {
console.log('outer click');
}
}).
controller('myInnerCtrl1', function ($scope) {
// nothing here!!
}).
controller('myInnerCtrl2', function ($scope) {
$scope.innerTwoClick = function () {
console.log('inner two click');
}
});
JSFIDDLE.
if you want to use the same function in two or more controllers you might need a service.
or use events
function firstCtrl($scope)
{
$scope.$broadcast('someEvent', [1,2,3]);
}
function secondCtrl($scope)
{
$scope.$on('someEvent', function(event, mass) { console.log(mass); });
}
Related
I have a controller that ive added a function to but cant get the function to be called from ng-click on an anchor. Ive looked at similar posts but cant really see what I could be missing. Its as though the controller function cant be seen?
The module and controller:
var commonModule = angular.module('common', ['ngRoute', 'ng.ckeditor']);
var mainModule = angular.module('main', ['common']);
//mainModule.config(function ($routeProvider, $locationProvider) {
// $locationProvider.html5Mode(true);
//});
commonModule.factory('viewModelHelper', function ($http, $q, $window, $location) {
return MyApp.viewModelHelper($http, $q, $window, $location);
});
commonModule.factory('validator', function () {
return valJs.validator();
});
mainModule.controller("rootViewModel",
function ($scope, $http, $q, $routeParams, $window, $location, viewModelHelper, $rootElement) {
//test
console.log("creating controller");
var self = this;
viewModelHelper.apiGet('api/PageContent/1', null,
function (result) {
$scope.htmlEditor = result.data;
});
$scope.ToggleEditor2 = function () {
//test
console.log("hello");
if ($scope.editorVisible == true) {
$scope.editorVisible = false;
}
else {
$scope.editorVisible = true;
}
}
});
The controller is referenced at the root level of the page:
Which in this case since im using ASP.Net MVC, is in my _layout.cshtml
<body data-ng-app="main" data-ng-controller="rootViewModel">
In a (mvc) view that gets loaded, I have a button with ng-click that calls the ToggleEditor2 function, but its never called. Cant get a breakpoint to hit in the chrome dev console and I dont see anything written to the log either.
<input type="button" ng-click="ToggleEditor2()" value="test me" />
Update:
If I wrap that anchor with a div and specify the "rootviewModel" controller there, the log message gets written. Hmmm - something tells me its related to scope?
<div data-ng-controller="rootViewModel">
<input type="button" ng-click="ToggleEditor2()" value="test me" />
</div>
Problem
A common problem with $scope is that when using any sort of nested controllers or modules, your $scope's can step on each other and cause issues like you have experienced.
Solution
Using Angular's "Controller As" syntax is the recommended solution for this problem. It allows you to create multiple instances of the same controller, while defining and maintaining a unique scope for each instance.
This Article is a great resource I used to understand and implement this new syntax.
I have small problem to solve.
I have modal controller rejectIssueModalCtrl.js
(function () {
'use strict';
function rejectIssueModalCtrl($modalInstance, issue, $rootScope) {
var self = this;
self.cancel = function () {
$modalInstance.dismiss('cancel');
};
self.reject = function ($rootScope) {
$modalInstance.close(self.reason);
console.log(self.reason);
};
$rootScope.reasono = self.reason;
}
rejectIssueModalCtrl.$inject = ['$modalInstance', 'issue', '$rootScope'];
angular
.module('app')
.controller('rejectIssueModalCtrl', rejectIssueModalCtrl);
})();
When I click the button I can open this modal and write a reason. I want to show this reject reason in table in other controller.
Here's my code from other controller issueDetailsCtrl.js
$scope.reasonoo = $rootScope.reasono;
function rejectIssue() {
var rejectModal = $modal.open({
templateUrl: '/App/Issue/rejectIssueModal',
controller: 'rejectIssueModalCtrl',
controllerAs: 'rejectModal',
size: 'lg',
resolve: {
issue: self.issueData
}
});
rejectModal.result.then(function (reason) {
issueSvc
.rejectIssue(self.issueData.id, reason)
.then(function (issueStatus) {
changeIssueStatus(issueStatus.code);
getIssue();
}, requestFailed);
});
};
and html code
<div>
<span class="right" ng-bind="$root.reasono"></span>
</div>
As you can see I tried to use $rootScope. I can console.log the reason but I cant make it to show in this html. Any help?
We're missing some context, but I believe this is your problem:
self.reject = function ($rootScope) {
$modalInstance.close(self.reason);
console.log(self.reason);
};
$rootScope.reasono = self.reason;
Assuming self.reason is bound to the input in your modal, it won't be defined outside of the reject callback - that's the nature of async. You're able to log to console because you're doing so within the callback.
Define $rootScope.reasono inside of the callback like so:
self.reject = function () {
$modalInstance.close(self.reason);
console.log(self.reason);
$rootScope.reasono = self.reason;
};
Edited to show that $rootScope should be removed as a named parameter in the reject function definition.
Using root scope is not recommended. For this reason it is recommended to create a service for intercommuncation with variable to store reject reason, then inject this service for each controller - that way you will be able to read/write reason from different controllers.
This question already has answers here:
Pass value in-between angular JS controller and services
(3 answers)
Closed 7 years ago.
I want to get the variable value which has been assigned inside ng-click function
mainApp.controller('setSearchController',function($scope) {
$scope.getQuery = function(userq) //ng-click function
{
$scope.userq=userq;
};
alert($scope.userq); // showing Undefined even after the click is made
// Once clicked I want to use **userq** value to make one $https call
// apart from services,How to use userq here outside getQuery scope
});
before using ng-click , its ok to get undefined value but what I am trying to do is to use the same controller for another view , which will be rendered after the ng-click happens
.when('/search', {
templateUrl: "../static/views/seachResult.html",
controller: "setSearchController"
})
so I want to fetch some searched data in searchResult.html view after $https call
Take a look at my answer here:
Pass value in-between angular JS controller and services
It is the same question, I have solved your problem with event listeners using $on and $emit
HTML:
<div ng-app="myApp" ng-controller="myCtrl">
<button ng-click="getQuery('myVal')">Test</button>
<button ng-click="consoleIt(myVal)">Console</button>
</div>
Controller:
angular.module('myApp', [])
.controller('myCtrl', ['$scope', function($scope) {
$scope.getQuery = function(a){
$scope[a] = a;
}
$scope.consoleIt = function(a){
console.log(a);
}
}]);
The getQuery function creates a new property on your scope. The property's name is equal to what is passed (string) as agrument to the function. consoleIt simply console logs its agrument. myVal is undefined until you click the test button. You can also pass a variable (instead of string) to getQuery, in this case just remove the quotes from inside of parenthesis.
Move alert inside statement function getQuery.
mainApp.controller('setSearchController', function($scope) {
$scope.userq = null;
$scope.getQuery = function(userq) //ng-click function
{
$scope.userq=userq;
alert($scope.userq);
};
});
or this solution
mainApp.controller('setSearchController', ['$scope', '$window', setSearchController]);
function setSearchController ($scope, $window) {
$scope.userq = null;
$scope.getQuery = function(userq) //ng-click function
{
$scope.userq=userq;
$window.alert($scope.userq);
};
}
So let's say I have a controler that dppends on a news service. Whenever news is published, the controller would like to diisplay the latest news. I'd rather not use $broadcast and $on, because this does weird things to the way components are coupled. Components which don't depend on news could still listen for these events.
So, here's what I'd like to be able to do:
myApp.controller("myController", ["news", function(news){
news.onPublish.addListener(function(story){
... Show the latest juicy story.
});
}]);
news.onPublish would be an Event instance, with Event defined as follows:
Event = function(){
var listeners = [];
this.addListener = function(l){
listeners.push(l);
}
this.trigger = function(){
var params = arguments
listeners.map(function(l){
l.apply(undefined, params);
});
}
return this
}
Is this a good way to implement communication between services and other components? Also, would it be good to call $rootScope.$apply at the end of Event.trigger so that any changes the listeners made will be picked up?
The best is to write common data storing factories that do the job for you. Here's a working example: http://jsfiddle.net/9L5xL8sx/ which shows how this works. The NewsService factory can be used across several Angular modules, and within the same module, as shown in my example.
Here's the JS:
var app = angular.module("TestSharing", []);
app.factory("NewsService", [function() {
var articles = [];
var makeNews = function (text) {
articles.push({text: text});
};
var getNews = function() {
return articles;
};
return {
get: getNews,
make: makeNews
};
}]);
app.controller("FirstCtrl", ["$scope", "NewsService", function($scope, NewsService) {
$scope.articles = function () {
return NewsService.get();
};
}]);
app.controller("SecondCtrl", ["$scope", "NewsService", function($scope, NewsService) {
$scope.articletext = "";
$scope.submit = function () {
NewsService.make($scope.articletext);
};
}])
The HTML:
<div ng-app="TestSharing">
<div ng-controller="FirstCtrl">
<span ng-repeat="article in articles()">{{article.text}}<br/></span>
</div>
<div ng-controller="SecondCtrl">
<input type="text" ng-model="articletext"/>
<button ng-click="submit()">Make some news</button>
</div>
</div>
Also, services like these can also share events. For example, if you'd used the factory to expose an object called somethingNew, which merely contained if something new had happened in one of the Controllers it had been shared in, you could achieve the same effect. The idea would be to only listen for changes where you'd want (using something like $scope.$watch(NewsService.somethingNew, function (now, then) {…})) and that would be just as easy.
I have seen a few exmaples on stack overflow about this ng-init issue, although I cant seem to find one which references it with the use of a controller.
I have called the function in the controller by having the following in the html file
<div class="tab-container" ng-controller = "ExampleController" ng-init = "init()" >
In the controller:
$scope.init = function(){
alert("do something");
};
It does run, but it runs before the components have loaded on the screen.
Am i missing something?
Thanks
ng-init is supposed to work like this, because it's used to initialize data.
A very simple example:
<ul ng-init="list = [1,2,3,4]">
<li ng-repeat="l in list"></li>
</ul>
If you are trying to run something while your controller loads, it's actually much simpler than you thought:
app.controller('mainCtrl', function ($scope) {
var init = function ($scope) {
// do whatever you need to do to initialize your controller
$scope.someData = ["Hey", "I'm", "Alive"]
$scope.otherData = localStorage.getItem('myBackup')
}
init()
})
Or even simpler, if you don't need the function (no closures or whatever)
app.controller('mainCtrl', function ($scope) {
// do whatever you need to do to initialize your controller
$scope.someData = ["Hey", "I'm", "Alive"]
$scope.otherData = localStorage.getItem('myBackup')
})
Edit - assuming you're using ngView:
To have the code run on when the page is fully loaded you should set a watcher on the event $viewContentLoaded, like this:
$scope.$on('$viewContentLoaded', function(){
//Here your view content is fully loaded !!
});
app.controller('mainCtrl', function ($scope) {
// This event is triggered when the view has finished loading
$scope.$on('$viewContentLoaded', function() {
$scope.someData = ["Hey", "I'm", "Alive"]
$scope.otherData = localStorage.getItem('myBackup')
})
})
another option is using jquery. It would fit if you depend on many elements. But make sure to load jquery with a version of your choice to project.
loading jquery (insert version where it's ...):
<script src="https://code.jquery.com/jquery-..."></script>
the js code:
$(document).ready(function() {
alert("do something");
});