I have an angularjs app with some controllers.
At some point, I need to access a function defined inside a controller, but the place where the function is gonna be called is not inside the angularjs APP.
I'll try to build a simple scenario:
app.controller('TaskController', function ($scope, $routeParams, $window, TaskEngine, PortalUtil) {
$scope.closeTask = function() {
$scope.openTask = false;
$scope.openedTaskUrl = undefined;
}
});
-- Some other place in my web app, outside of the angular APP.
<button onclick="closeTask();">
The "closeTask()" function is never accessible, because its out of scope.
I tried to define it in the window object
$window.closeTask = function() {
$scope.openTask = false;
$scope.openedTaskUrl = undefined;
}
});
Now, my function is visible, but the variables "openTask" and "openedTaskUrl" are not.
Is it possible to do what I want?
Basically, I have a controller, and I need to access one function anywhere, to control the behaviour of a menu.
Thanks!
I would not use $window for this. Instead, Angular has a nice feature which lets not Angular code to interact with it. Using Angular.element you can access data or functions defined in Angulars Scope.
var $scope = angular.element(document.querySelector('[ng-app=app]')).scope();
$scope.$apply(function() {
$scope.print("Hello world");
});
Related
Today I was working to migrate my existing Angular js ngRoute base code to ngNewRouter and got stuck in component controller. As per new apporach component controller doesn't have $scope, instead of it uses "this". It works fine for simple usecase. In my usecase for a particular component I make REST API call to get the data. All works but when callback happens then it is not identifying "this" defined object. Following is code snippet for more details.
Component Controller to make call to get data:
var ClubonboardingController = function ($location, ApplicationLoaderService, ApplicationConstants, $window, ResourceImplementation, $rootScope) {
//Launch Application
this.launchApplication = function () {
this.cacheLoaded = true;
};
//Method to initialize
this.init = function () {
//show loading screen
this.showLoadingScreen = true;
//Initilize Application cache
ApplicationLoaderService.initilizeApplicationCache($rootScope, "3252345", true, "en_US", function () {
this.launchApplication(); //This throws function not found exception
});
};
//Call init() to initialze the loading.
this.init();
};
app.controller("ClubonboardingController", ClubonboardingController);
ClubonboardingController.$inject = ['$location', 'ApplicationLoaderService', 'ApplicationConstants', '$window', 'ResourceImplementation', '$rootScope'];
Issue: After callback code thinks "this" as global javascript object, instead of "this" for this controller and not able to find anything defines in this controller and it throw exception at this.launchApplication(); .
Please help.
I think the problem is that what 'this' means changes depending on context. There seems to be a general recommendation to assign 'this' to a local variable and use the variable. Thanks to JavaScript closures, the value of 'this' is then retained.
Using a local variable called vm seems to be common, and replace all 'this.' with 'vm.' Your code would end up changing to
var ClubonboardingController = function ($location, ApplicationLoaderService, ApplicationConstants,
$window, ResourceImplementation, $rootScope) {
var vm = this;
//Launch Application
vm.launchApplication = function () {
vm.cacheLoaded = true;
};
//Method to initialize
vm.init = function () {
...
Hope this helps.
I'm building a small two-language app with the use of angular-translate. I want to have a language switcher in every view (controller). I'm trying to figure out how to put the code responsible for language switching into every controller. The code looks like this:
var langSwitch = $Scope.setLang = function (langKey) {
$translate.use(langKey);
};
So far I've figured that I can create a factory that looks like this:
app.factory('langSwitch', function ($rootScope, $translate) {
var langSwitch = $rootScope.setLang = function (langKey) {
$translate.use(langKey);
};
return langSwitch;
});
and inject it into controllers in this maner:
app.controller('HomeCtrl', function (langSwitch) {
// normal controller code here
});
This works but 1) I'm using $rootScope and I have a feeling this is bad practice & 2) jsHint screams that "langSwitch" is not defined. Maybe there is a simpler way to make the function global without putting it into every controller?
I'm still pretty new to Angular so don't scream at me :) Thanks.
edit
My view:
<button ng-click="setLang('en_GB')">English</button>
<button ng-click="setLang('pl_PL')">Polish</button>
Although you got the idea, you overcomplicated things a bit. You could declare the service as follows:
app.service('langSwitch', function ($translate) {
this.setLang = function (langKey) {
$translate.use(langKey);
};
});
And then inject langSwitch in the controller responsible for lang switching, as you already did. No need to inject $rootScope in the service.
You don't need $rootScope indeed unless you need to process some global events in your application. All services and factories in angular are singletons by default. That means once it created, it will be passed as the same instance in every place it is declared as a dependency. So if you want to share data and functionality between different controllers - the services will suit fine. You can change your factory code to:
app.factory('langSwitch', function($translate) {
return {
setLang: function(langKey) {
$trasnlate.use(langKey);
};
};
});
I have a directive which is associated with one controller and the functions in my controller defined as
MyFormController.prototype.addNewRow = function addNewRow() {
//Adding row code
};
I want to call this method from another controller, possible ways?
I ve user the service and moved the code into that service which is shared across the controllers, however the service code does the DOM manipulation, and then i guess the next question would be that can we use $compile in a service test case
service or factory is used to share data between controller.so it would be best to define function in service and factory.
demo:
(function() {
angular.module('app', [])
.service('svc', function() {
var svc = {};
svc.method = function() {
alert(1);
}
return svc;
})
.controller('ctrl', [
'$scope', 'svc', function($scope, svc) {
svc.method();
}
]);
})();
You should not!!!
That defeats the whole purpose of modularity.
If possible try to make the function generic and create a service/factory. Now both the places where you need, use the same generic function defined in service and do their stuff.
Otherwise you can also look at events to make changes accordingly.
Look at this blog post:
http://ilikekillnerds.com/2014/11/angularjs-call-controller-another-controller/
Last but the worst solution is (avoid using this, this is literally an aweful way) is catching the element inside directive and getting its scope and taking the function from it.
Example,
var otherControllerFunc = $(".inside-directive").scope().yourfunc;
I've got a potentially really dumb question, but how do I modify variables up in $rootScope in Angular? I've got a slide-in sidebar that I want to change the content on whenever someone clicks on a thumbnail, and I figured the easiest way to handle where the data in the sidebar comes from/the sidebar visibility would either be in global values, or in $rootScope. I'm trying to keep everything as simple as possible, but I just don't know how to handle modifying global variables.
My angular code surrounding this is:
app.run(function($rootScope) {
$rootScope.currentUrl = { value: 'visual/design/1/' };
$rootScope.detail_visible = { value: true };
});
app.controller('navController', ['$scope', '$rootScope',
function ($scope, $rootScope) {
$scope.isDetail = $rootScope.detail_visible.value;
$scope.url = $rootScope.currentUrl.value;
$scope.hide = function($rootScope) {
$rootScope.detail_visible.value = false;
};
}]);
and the connecting HTML is
<div id="detail_box" ng-class="{d_show: isDetail, d_hide: !isDetail}">
<div ng-include="url + 'detail.html'"></div>
</div>
In essence, I'm trying to make it so that when you click on a thumbnail, it changes the currentUrl value from 'visual/design/1/' to whatever they've clicked on (like, 'music/solo/2' or whatever) then changes the value of detail_visible to false, so that the classes on my sidebar switch and I get a nice little slide-in, with fresh content loaded via ng-include which I kind of love a thousand times more than I thought I would. I've been banging my head against this for about three hours now, breaking everything else on this app whenever I get the chance. What am I screwing up here? Alternatively, is there a better way of doing this?
My reason for using global variables is that I have multiple thumbnails in multiple controllers, and I want each one to be able to dynamically change the URL in my ng-include.
For your question, you change the $rootScope variable simple by referencing it with
$rootScope.detail_visible.value = newValue;
but you dont need to inject $rootScope to your function:
$scope.hide = function() { //without $rootScope
$rootScope.detail_visible.value = false;
};
But, I would suggest you to implement a service and not to pollute the rootscope for such task.
https://docs.angularjs.org/guide/services
Object properties of scopes are inherited -- in your controller, you should be able to modify $scope.detail_visible.value and see it affect the $rootScope. You still have to initialize it on the $rootScope in .run() though.
app.run(function($rootScope) {
$rootScope.currentUrl = { value: 'visual/design/1/' };
$rootScope.detail_visible = { value: true };
});
app.controller('navController', ['$scope', function ($scope, $rootScope) {
$scope.hide = function() { // don't need to pass an argument
$scope.detail_visible.value = false;
};
}]);
view:
<div id="detail_box" ng-class="{d_show: currentUrl.value, d_hide: !currentUrl.value}">
<div ng-include="currentUrl.value + 'detail.html'"></div>
</div>
I have a function that I will attach to my scope like this. It's attached to the scope as I use this function in my HTML pages Presently I am doing this in more than one controller. Note that my controllers are all top level controllers so I cannot really put this in a higher up controller and have it inherited.
$scope.isNotString = function (str) {
return (typeof str !== "string");
}
I asked how I could share this functionality and was given the following
example:
app.service('myService',function(){
this.sharedFunction = function() {
//do some stuff
};
});
myCntrl1($scope,myService) {
$scope.doSomething = function() {
myService.sharedFunction();
}
}
myCntrl2($scope,myService) {
$scope.doSomething = function() {
myService.sharedFunction();
}
}
Is there a way that I could more directly share it by passing
in $scope to the service and in that way eliminating the need for:
$scope.doSomething = function() {
myService.sharedFunction();
}
In each controller.
You can attach the function to the parent (root) scope, but using a service is the preferred way of sharing code between controllers.
You could call myService.init($scope) in the controller and that function could append properties to the scope but more likely you would want to use a parent controller from which you inherit.
You either declare a shared function in a top level "controller" or in a "service" like you mentioned in your example. There's no other better way so far.
You can assign the scope to a global variable, that will expose that scope globally, but will behave the same way like "service", except you don't have to inject it like service, rather can call by globalVar.dosomething().