I was wondering if there is a way to expand my angular controller on the fly according to the user's permissions.
For example lets say that we have three different permission levels respectively (Admin,Supervisor,User) and an angular controller
app.controller("myController", ["$scope", function($scope){
$scope.userFunc = function(){....};
$scope.supervisorFunc = function(){....};
$scope.adminFunc = function(){....};
}]);
I would like to add the supervisorFunc and the adminFunc only if the user possest the right permission.
User controller will contain only userFunc
Supervisor will contain userFunc and supervisorFunc
Admin will contain all the methods (userFunc,supervisorFun and adminFunc)
Now I would like to add a separate js file that will inject the additional methods into the controller, so if you are a Supervisor your client will receive two js files, one is the main myController and an additional Supervisor Injector that will expand myController.
One important thing is that I don't want to dynamically generate myController js file at server side because it's bad practices.
any ideas ?
I like the idea, and we do the same thing (sort of).
IF the second set of controller items does not have new requirements (directives and such), you should be able to do this:
in app.js:
var app = angular.module('myModule', ["whatever-directive", "some-service"]);
app.config(['$httpProvider', '$locationProvider', function ($httpProvider, $locationProvider) {
..
..
}]);
..
..
in controllers.js
function someCtrl($scope, $location, $http) {...}
function someOtherCtrl($scope, $location, $http) {...}
in onlyForYouControllers.js - that is only added if needed
function someCtrlOnlyForYou($scope, $location, $http) {...}
Like I wrote - you must declare what ever needed in the app to start with - even if it not used by a particular set of controllers.
Related
I have an MVC application that has a master layout that has a #RenderBody() inside of it that renders the body of the individual page. In the master layout I have given the body tag an angularjs app name and controller name.
I want the controller to be defined in a separate global angular.js file. This will share common functions that every page can take advantage of.
myApp.controller('myController', ['$scope', '$http', function ($scope, $http) {
$scope.sharedFunction = //...shared function stuff
})';
I also want to add specific scope functions and variables in the child pages in MVC. When I get to the child pages, I believe I end up redefining the controller if I use the same syntax.
myApp.controller('myController', ['$scope', '$http', function ($scope, $http) {
$scope.showOverlay = false;
}]);
How can I make it so the same controller that is defined in the body gets page specific items added to it in the child pages that are rendered using #RenderBody()?
You need to define your 'app' as a module in Angular, like so:
var app = angular.module("app", [myController])
The 2nd part of the declaration allows you to pass in an array of 'dependencies' that your Angular app relies on. In this case, your app will include your 'myController' controller
Then use this attribute on the part of your html that you want Angular to act upon:
ng-app="app"
Doing this will make your controller available to any page that utilises your 'app' module.
I got this to work by nesting ng-controllers. The second controller inherited all of the first controller functions and I was able to overwrite them and add to them in the second controller.
Well I have seen couple of questions in StackOverFlow related to this but couldn't find proper answer.
Say in my app.js file I have defined a constants object which basically has controllers names. As I use "ui-router" I want to attach Controllers to views on state change.
app.js
var app = angular.module('myApp', ['ui.router']);
app.constant('CONTROLLER', {
ONE: 'ControllerOne',
TWO: 'ControllerTwo'
});
Now I want to use those constants to define Controller names in other file, say controller.js. Illustrating couple of ways which I tried but dint work for me.
controller.js
var app = angular.module('myApp', ['CONTROLLER']);
app.controller(CONTROLLER.ONE, ['$scope', 'myFactory', function ($scope, myFactory) {
$scope.result = myFactory.someAPI();
}]);
ERROR -> Uncaught ReferenceError: CONTROLLER is not defined
Even I tried injecting that constant module in Controller which gave same error.
var app = angular.module('myApp', ['CONTROLLER']);
app.controller(CONTROLLER.ONE, ['$scope', 'myFactory', 'CONTROLLER', function ($scope, myFactory, CONTROLLER) {
$scope.result = myFactory.someAPI();
}]);
I know that second way is wrong. As I define controller names in my main app.js file and use those constants to attach controllers to views during state change.
I want reuse those constants to define controllers names too.
I may be doing something wrong. Any suggestions ?
Your code shows that you have correctly injected the CONTROLLER constant into the controller constructor function.
var app = angular.module('myApp', ['CONTROLLER']);
app.controller(CONTROLLER.ONE, ['$scope', 'myFactory', 'CONTROLLER', function ($scope, myFactory, CONTROLLER) {
// use constant here
}]);
That being said you can only access the constant except from within the controller function itself. The names of controllers, services, factories etc must be accessible strings, which is not the case until the constant has been injected.
You could use a plain old JavaScript object outside angular to define the names.
var CONTROLLER = {
ONE: 'ControllerOne',
TWO: 'ControllerTwo'
});
I can only guess that you are trying to do this because the controller or directive you are writing will be referenced/used at multiple points throughout your html.
An AngularJS app was successfully including a service (called auth) in one controller (called secure) and then allowing a view handled by the secure controller to interact with the auth service using the auth.methodname() syntax.
The app uses version 1.4.8 of AngularJS, and compiled properly with the $cookies.get('keyname') syntax every time until I tried to inject auth into a second controller (called navigation) and call one of its methods from a view handled by the second (navigation) controller.
All of the sudden, the app would not compile, and the FireFox developer tools console said that $cookies.get is not a valid method. Then after I changed the syntax to $cookies[''] (despite the fact that the app is still uses version 1.4.8 of AngularJS, the $cookies.get error went away, and a new error stating that $interval is not a valid function appeared to block compilation. All the compilation errors point to the auth module, whose code has not changed since before the error appeared. I am wondering if the problem is in the way that the auth service is being injected into the navigation controller. What specific changes need to be made to the code below so that the auth service can successfully be re-used by both the navigation controller and the secure controller?
Here is the relevant part of the auth service, which has not changed (except for changing $cookies.get('') to $cookies['']) since before the new problem emerged:
angular
.module('auth', ['ngCookies'])
.service('auth', ['$rootScope', '$http', '$cookies', '$interval', function($rootScope, $http, $location, $cookies, $interval) {
var $this = this;
this.authenticated1 = $cookies['AUTH1'];
//// other stuff
$interval(function(){
//stuff
}
}
Here is the navigation controller, which might have incorrectly injected auth, and thus possibly caused the problem:
angular
.module('navigation', ['auth', 'ngRoute'])
.controller('navigation', function($scope, auth, $route) {
$scope.auth = auth;
// other stuff
}
Here is the secure controller, which worked properly before the changes, and which has not changed since before the error appeared:
angular
.module('secure', ['auth'])
.controller('secure', function($scope, auth, $routeParams) {
$scope.auth = auth;
// other stuff
}
The calling code from someview handled by navigation controller did change to include a call to auth.logout() as part of the changes before the problem appeared. Here is the someview code:
<div ng-controller="navigation" class="container">
<ul class="nav nav-pills" role="tablist" >
<li ng-class="{active:tab('home')}">Home</li>
<li ng-class="{active:tab()}"><a ng-href='auth.logout()' ng-click='auth.logout()' >logout</a></li>
</ul>
</div>
The calling code from someotherview handled by secure controller did not change since before the problem appeared, but the unchanged code is:
<div ng-show="auth.authenticated1!='yes'">
Show something in particular specified here.
</div>
This should solve the problem:
angular
.module('auth', ['ngCookies'])
.service('auth', ['$rootScope', '$http', '$location', '$cookies', '$interval', function($rootScope, $http, $location, $cookies, $interval) {
You missed the $location declaration that you 're using in the function parameters. If you're using the array notation for the parameter injection, your function parameters must match.
I have an administrator login page. When a user successfully logs in as an admin, I want to display at the top of all pages the user visits:
<!-- nav bar -->
<div>
<span ng-show="$root.isAdmin">(ADMIN)</span>
</div>
I tried using $rootScope to accomplish this, but I keep getting undefined:
// controller for logging in
app.controller('AdminLoginCtrl', [ '$rootScope', '$scope', 'stateParams', 'loginService', function($rootScope, $scope, $stateParams, loginService) {
loginService.success(function() {
$rootScope.isAdmin = true;
console.log($rootScope.isAdmin); // returns true
$location.path("/login_success");
});
}]);
// some other controller AFTER user is logged in as admin
app.controller('BlahCtrl', ['$rootScope', '$scope', function($rootScope, $scope) {
console.log($rootScope.isAdmin); // returns 'undefined'
});
There have been some other similar topics, but don't apply to me:
(I don't have an injection problem):
$rootscope value undefined in Controller
(My other controller is definitely being called AFTER login, so $rootScope.isAdmin should be set):
AngularJS - Cannot access RootScope
(I don't have an ordering problem):
Angular $rootScope.$on Undefined
The other solution could be to not use $rootScope at all, and just use a service to hold shared data. But if I have 10+ controllers, is it acceptable to do this in each:
app.controller('...', ['$scope', 'checkAdminService', function($scope, checkAdminService) {
$scope.isAdmin = checkAdminService.getIsAdmin()
}]);
Then:
<span ng-show="isAdmin">(ADMIN)</span>
Is this acceptable or more cumbersome? And for completeness sake, why doesn't $rootScope solution work at all?
It's indeed a bad practice using the $rootScope. The best solution is what you propose yourself to create a service storing the information you want to share between different controllers. It is no problem to call this method on several locations...
Why don't you define a separate controller for the top-part of your page which will be the only one verifying whether the user is an admin? The other part of your page can be controller by different controllers.
This example will explain how to use several controllers, but of course this can also be achieved via Routers.
What if you replace the $rootScope that you're passing around with a special-purpose angular value that you can register at startup and pass around as needed?
http://plnkr.co/edit/r6kdouh0BbnYA4DmEcEM?p=preview
angular
.module('app')
.value('userProfile', userProfile);
function userProfile() {
var userProfile = {
isAdmin: false
};
return userProfile;
}
/////////////////
angular
.module('app')
.run(run);
run.$inject = ['userProfile', 'userAuthService'];
function run(userProfile, userAuthService) {
userAuthService.isUserAdmin().then(function(result) { userProfile.isAdmin = result; });
}
angular.module('yourmodule', [])
.run(['$rootScope', function($rootScope) {
$rootScope.isAdmin = true;
])
Suppose I want to make this a variable a constant to be shared among controllers in Angularjs;
$webroot = "localhost/webroot/app"
After some investigation, it seems services are the way to do. But what is the best way? Do I use a factory, service, value or something else?
The services.js from angularjs-master-seed is below;
angular.module('myApp.services', []).value('version', '0.1');
How do I modify it to have a constant $webroot that is sharable among controllers?
Can I do the following?
angular.module('myApp.services', [])
.value('version', '0.1')
.constant('webroot','localhost/webroot/app');
If it is ok, how do I call it in the controller?
Whats happens when you want more constants? How about adding a config object that you can inject wherever needed. As its a single file it's also much easier to have dev.config and prod.config files that can be swapped in and out at build time.
app.factory('Config', function(){
return{
webRoot: "localhost/webroot/app",
moreSettings: "abc"
};
});
If your variable have a constant value or is set once value is the right choice.
You can define it like this:
app = angular.module('myApp', []);
app.value('$webroot', 'localhost/webroot/app');
Now you can inject the service to your controller and use it:
app.controller('myController', ['$scope', '$webroot', function($scope, $webroot) {
$scope.webroot = $webroot;
}]);
Edit #1
To fit your updated question: You can use a constant the same way as a value:
app = angular.module('myApp', []);
app.constant('$webroot', 'localhost/webroot/app');
app.controller('myController', ['$scope', '$webroot', function($scope, $webroot) {
$scope.webroot = $webroot;
}]);