In my main controller I have a user service that returns the current user.
// Curr user
userService.getCurrentUser().then(user => {
mainCtrl.currUser = user;
});
Is there any way to be able to use the currUser variable in other controllers without injecting my service and calling this method over and over again in maybe 50 controllers?
Ex.
// My other awesome controller
console.log(currUser.fullName);
You could go for a LocalStorage or $rootScope. , if you are sure about not using Providers/Services
Sample:
myApp.controller('DemoController1', ['$scope', '$rootScope', function DemoController($scope,$rootScope) {
$rootScope.currUser.fullName ="test";
}]);
Then access it as,
myApp.controller('DemoController2', ['$scope', '$rootScope', function DemoController($scope,$rootScope) {
var fullName = $rootScope.currUser.fullName;
}])
you can use $rootScope like bellow:
mainCtrl.$rootScope.currUser = user;
and the other controller, you can recover this as bellow:
console.log(yourContrl.$rootScope.currUser.fullName);
You don't want you use controller injections? Use injector its the same like injections but also different :D ... like in this runnable fiddle demo. In that way you still can use services, factories, or components.
var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl', function ($scope) {
$scope.name = angular.injector(['data']).get('user').currUser.username;
});
angular.module('data', []).factory('user', function () {
return {
currUser: {
username: 'Alfred'
}
}
});
I'm finishing a web app and all works like a charm.
I use Grunt to join all my .js files in one unique file and that's the one I use in the index.html file to load the code.
The issue is when I use the .min. version of the file generated by grunt using 'grunt-contrib-uglify' task.
When I reload the page, the following error arises:
angular.js:38Uncaught Error: [$injector:modulerr] http://errors.angularjs.org/1.5.5/$injector/modulerr?p0=myapp&p1=Error%…ogleapis.com%2Fajax%2Flibs%2Fangularjs%2F1.5.5%2Fangular.min.js%3A39%3A222)
I've been reading around this on Google but no success.
How can I solve this?
EDIT:
This is a typical file controller (all have the same structure):
(function() {
var app = angular.module("post", []);
var controllers = {};
controllers.postCtrl = ['$scope', '$rootScope', 'myFactory', function($scope, $rootScope, myFactory) {
$scope.loading = {state:false};
$scope.filters = $scope.filters || myFactory.authors;
$scope.init = function() {
var idx = myFactory.get_author_by_index(Number($('input[name="author"]').val()));
$scope.filterSelected = $scope.filters[idx];
angular.element(document).ready(function () {
angular.forEach($('div.general_page_content').find('a'), function(value, index) {
$(value).attr('target', "_new");
})
angular.forEach($('div.general_page_content').find('iframe'), function(value, index) {
$(value).attr("width", "100%");
})
angular.forEach($('div.general_page_content').find('img'), function(value, index) {
$(value).attr("width", "100%").css('width', '100%');
})
myFactory.containerResize();
});
}
$rootScope.$on('loading', function(evt, value) {
$scope.loading.state = value;
});
$rootScope.$on('autocomplete:focus', function(ev) {
$scope.search_focus = true;
});
$rootScope.$on('autocomplete:blur', function(ev) {
$scope.search_focus = false;
});
$scope.showSocialShare = function(ev) {
$scope.url = decodeURIComponent($('input[name="url"]').val());
$scope.text = decodeURIComponent($('input[name="text"]').val());
$scope.img = decodeURIComponent($('input[name="img"]').val());
myFactory.showSocialShare($scope, ev);
};
$scope.favorite_post = function(ev, id, title) {
myFactory.favorite_post($scope, ev, id, title);
}
$scope.fetchPostsChange = function(selected) {
document.location = '/blog/?author='+selected.id;
}
$scope.search = function(text) {
document.location = '/blog/?search='+encodeURIComponent(text);
}
$scope.go_to_favorites_post = function() {
document.location = '/blog/archive/';
}
$scope.init();
}];
app.controller(controllers);
})();
UPDATE:
I took only two .js files and process them to minified it and check if the same error arises or not. The curious thing is that taking into account only two files, the same error arises, so I paste the minified file for you to be able to detect what's wrong.
!function(){angular.module("myapp",["ngMaterial","ngMessages","ngStorage","toaster","ngMdIcons","lvl.services","smart-table","angularGrid","ngFileUpload","angular-timeline","header","dashboard","sidebar","autocomplete","timeline","sidebarCollection","myappFactory","objectCtrl","homeCtrl","Collections","Collection","posts","post","model","postArchive","720kb.socialshare","services","footer"]).config(function(a,b,c){a.theme("default").primaryPalette("lime").accentPalette("grey").warnPalette("red"),a.theme("darkTheme").primaryPalette("lime").accentPalette("grey").warnPalette("red").dark(),b.enabled(!1),c.get("user")})}(),function(){var a=angular.module("autocomplete",[]),b={},c={};b.autocompleteCtrl=["$http","$scope","$mdBottomSheet","$sessionStorage","myappFactory",function(a,b,c,d,e){b.init=function(){b.session=d,angular.isUndefined(b.session.advance_search)&&(b.session.advance_search={select_all:!0,show_cost:!0,show_free:!0,items:[{name:"Thingiverse",selected:!0},{name:"Youmagine",selected:!0},{name:"MyMinifactory",selected:!0},{name:"Cults 3D",selected:!0},{name:"Pinshape",selected:!0},{name:"Turbosquid",selected:!0},{name:"Shapeways",selected:!0},{name:"GrabCAD",selected:!0},{name:"CGTrader",selected:!0},{name:"Threeding",selected:!0}]})},b.querySearch=function(c){var d=c.trim();return d&&d.length>2?(b.isFetching=!0,a.get(e._myapp_link+"/api/index.php/myapp/autocomplete/"+encodeURIComponent(d)).then(function(a){return a.data})):void 0},b.collectionSearch=function(c){var d=c.trim();return d&&d.length>2?(b.isFetching=!0,a.get(e._myapp_link+"/api/index.php/myapp/collection_search/"+encodeURIComponent(d)).then(function(a){return a.data})):void 0},b.search=function(a){var c="";b.session.advance_search.show_cost&&!b.session.advance_search.show_free?c+=" free:0 ":!b.session.advance_search.show_cost&&b.session.advance_search.show_free&&(c+=" free:1 "),angular.forEach(b.session.advance_search.items,function(a,d){(b.session.advance_search.select_all||a.selected)&&(c+=" plataforma:"+a.name)}),window.location="/?search="+encodeURIComponent(a)+"¶ms="+Base64.encode(c)},b.go_to_collection=function(a){window.location="/collections/"+encodeURIComponent(a)},b.showAdvancedSearch=function(){c.show({templateUrl:"/advanced_search_sheet.html",controller:"ListBottomSheetCtrl"})},b.init()}],b.ListBottomSheetCtrl=["$scope","$mdBottomSheet","$sessionStorage","myappFactory",function(a,b,c,d){a.session=c,a.toggle_all_sites=function(){a.session.advance_search.select_all=!a.session.advance_search.select_all,a.session.advance_search.select_all&&angular.forEach(a.session.advance_search.items,function(a,b){a.selected=!0})},a.toggle_advance_search=function(b){if(a.session.advance_search.select_all)a.session.advance_search.items[b].selected=!0,d.showMessage({msg:"Uncheck 'All repositories' first!"});else if(a.session.advance_search.items[b].selected=!a.session.advance_search.items[b].selected,!a.session.advance_search.items[b].selected){var c=0;angular.forEach(a.session.advance_search.items,function(a,b){a.selected&&++c}),c||(a.session.advance_search.items[b].selected=!0,d.showMessage({msg:"There must be at least 1 respository selected!"}))}},a.free_cost_checked=function(b){var c="cost"==b?!a.session.advance_search.show_cost:!a.session.advance_search.show_free;c?"cost"==b?a.session.advance_search.show_cost=!0:a.session.advance_search.show_free=!0:"cost"==b?a.session.advance_search.show_free?a.session.advance_search.show_cost=!1:d.showMessage({msg:"Free and Price cannot be unchecked!"}):"free"==b&&(a.session.advance_search.show_cost?a.session.advance_search.show_free=!1:d.showMessage({msg:"Free and Price cannot be unchecked!"}))}}],c.myEnter=function(){return function(a,b,c){b.bind("keydown keypress",function(b){13===b.which&&(a.$apply(function(){a.search(a.searchText)}),b.preventDefault())})}},c.onBlur=["$rootScope","$mdUtil","$timeout",function(a,b,c){return{require:"^mdAutocomplete",link:function(d,e,f,g){c(function(){var c=(e.find("input"),e[0],g.blur),h=g.focus;g.blur=function(){c.call(g),b.nextTick(function(){a.$broadcast("autocomplete:blur"),d.$eval(f.mdBlur,{$mdAutocomplete:g})})},g.focus=function(){h.call(g),b.nextTick(function(){a.$broadcast("autocomplete:focus"),d.$eval(f.mdFocus,{$mdAutocomplete:g})})}})}}}],a.controller(b).directive(c)}();
As you certainly found on Google, that error depends on Variable Mangling https://github.com/gruntjs/grunt-contrib-uglify#mangle.
So, when you mangle $rootScope becomes a and, of course, angular dependency injection cannot resolve it:
angular
.module('test', [])
.run(function($injector) {
console.log(
"$rootScope exists?",
$injector.has('$rootScope')
);
try {
// mangle $rootscope => a
$injector.get('a');
} catch(e) {
console.log('a exists?', e.message);
}
})
;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.js"></script>
<section ng-app="test"></section>
There are many ways to manage this issue:
Disable Variable Mangling (not a good way, but is a solution).
Always use Angular DI Annotation
a. Array notation: someModule.run(["$rootScope", function(a) {}]);
b. $inject Property: runFn.$inject = ['$rootScope']; function runFn(a) {}; someModule.run(runFn);
Use ngAnnotate that does annotation at build time. I suggest you this option because you don't need to take care about annotation...
Important thing:
Always run your code in angular strict di mode, that gives you the opportunity of control each annotation issue.
If you minify, each module's injections must be named. So before you may have had:
app.factory('myFactory', function($route) {
});
Now you must name/declare them as well otherwise angular doens't know what to inject:
app.factory('myFactory', ['$route', function($route) {
}]);
Why?
Minification turns everything to small variables amongst other things. So our factory becomes:
app.factory('myFactory', function(a) {
a.<functioncall>
});
Angular doens't know what "a" is and so must be told:
app.factory('myFactory', ['$route', function(a) {
a.<functioncall>
}]);
And so it now knows and a= $route and so on.
I don't know what it is about injecting factories, but I am having the most difficult time.
I've simulated what I'm attempting to do via this sample plunk http://plnkr.co/edit/I6MJRx?p=preview, which creates a kendo treelist - it works fine.
I have an onChange event in script.js which just writes to the console. That's also working.
My plunk loads the following:
1) Inits the app module, and creates the main controller myCtrl (script.js)
2) Injects widgetLinkingFactory int myCtrl
3) Injects MyService into widgetLinkingFactory
The order in which I load the files in index.html appears to be VERY important.
Again, the above plunk is NOT the real application. It demonstrates how I'm injecting factories and services.
My actual code is giving me grief. I'm having much trouble inject factories/services into other factories.
For example,
when debugging inside function linking() below, I can see neither 'CalculatorService' nor 'MyService' services. However, I can see the 'reportsContext' service.
(function () {
// ******************************
// Factory: 'widgetLinkingFactory'
// ******************************
'use strict';
app.factory('widgetLinkingFactory', ['reportsContext', 'MyService', linking]);
function linking(reportsContext, MyService) {
var service = {
linkCharts: linkCharts
};
return service;
function linkCharts(parId, widgets, parentWidgetData) {
// *** WHEN DEBUGGING HERE, ***
// I CANNOT SEE 'CalculatorService' AND 'MyService'
// HOWEVER I CAN SEE 'reportsContext'
if (parentWidgetData.parentObj === undefined) {
// user clicked on root node of grid/treelist
}
_.each(widgets, function (wid) {
if (wid.dataModelOptions.linkedParentWidget) {
// REFRESH HERE...
}
});
}
}
})();
A snippet of reportsContext'service :
(function () {
'use strict';
var app = angular.module('rage');
app.service('reportsContext', ['$http', reportsContext]);
function reportsContext($http) {
this.encodeRageURL = function (sourceURL) {
var encodedURL = sourceURL.replace(/ /g, "%20");
encodedURL = encodedURL.replace(/</g, "%3C");
encodedURL = encodedURL.replace(/>/g, "%3E");
return encodedURL;
}
// SAVE CHART DATA TO LOCAL CACHE
this.saveChartCategoryAxisToLocalStorage = function (data) {
window.localStorage.setItem("chartCategoryAxis", JSON.stringify(data));
}
}
})();
One other point is that in my main directive code, I can a $broadcast event which calls the WidgetLinking factory :
Notice how I'm passing in the widgetLinkingFactory in scope.$on. Is this a problem ?
// Called from my DataModel factory :
$rootScope.$broadcast('refreshLinkedWidgets', id, widgetLinkingFactory, dataModelOptions);
// Watcher setup in my directive code :
scope.$on('refreshLinkedWidgets', function (event, parentWidgetId, widgetLinkingFactory, dataModelOptions) {
widgetLinkingFactory.linkCharts(parentWidgetId, scope.widgets, dataModelOptions);
});
I am wasting a lot of time with these injections, and it's driving me crazy.
Thanks ahead of time for your assistance.
regards,
Bob
I think you might want to read up on factories/services, but the following will work:
var app = angular.module('rage')
app.factory('hi', [function(){
var service = {};
service.sayHi = function(){return 'hi'}
return service;
}];
app.factory('bye', [function(){
var service = {};
service.sayBye = function(){return 'bye'}
return service;
}];
app.factory('combine', ['hi', 'bye', function(hi, bye){
var service = {};
service.sayHi = hi.sayHi;
service.sayBye = bye.sayBye;
return service;
}];
And in controller...
app.controller('test', ['combine', function(combine){
console.log(combine.sayHi());
console.log(combine.sayBye());
}];
So it would be most helpful if you created a plunk or something where we could fork your code and test a fix. Looking over your services it doen't seem that they are returning anything. I typically set up all of my services using the "factory" method as shown below
var app = angular.module('Bret.ApiM', ['ngRoute', 'angularFileUpload']);
app.factory('Bret.Api', ['$http', function ($http: ng.IHttpService) {
var adminService = new Bret.Api($http);
return adminService;
}]);
As you can see I give it a name and define what services it needs and then I create an object that is my service and return it to be consumed by something else. The above syntax is TypeScript which plays very nice with Angular as that is what the Angular team uses.
I'm trying to call a web service in AngularJS bootstrap method such that when my controller is finally executed, it has the necessary information to bring up the correct page. The problem with the code below is that of course $rootScope is not defined in my $http.post(..).then(...
My response is coming back with the data I want and the MultiHome Controller would work if $rootScope were set at the point. How can I access $rootScope in my angular document ready method or is there a better way to do this?
angular.module('baseApp')
.controller('MultihomeController', MultihomeController);
function MultihomeController($state, $rootScope) {
if ($rootScope.codeCampType === 'svcc') {
$state.transitionTo('svcc.home');
} else if ($rootScope.codeCampType === 'conf') {
$state.transitionTo('conf.home');
} else if ($rootScope.codeCampType === 'angu') {
$state.transitionTo('angu.home');
}
}
MultihomeController.$inject = ['$state', '$rootScope'];
angular.element(document).ready(function () {
var initInjector = angular.injector(["ng"]);
var $http = initInjector.get("$http");
$http.post('/rpc/Account/IsLoggedIn').then(function (response) {
$rootScope.codeCampType = response.data
angular.bootstrap(document, ['baseApp']);
}, function (errorResponse) {
// Handle error case
});
});
$scope (and $rootScope for that matter) is suppose to act as the glue between your controllers and views. I wouldn't use it to store application type information such as user, identity or security. For that I'd use the constant method or a factory (if you need to encapsulate more logic).
Example using constant:
var app = angular.module('myApp',[]);
app.controller('MainCtrl', ['$scope','user',
function ($scope, user) {
$scope.user = user;
}]);
angular.element(document).ready(function () {
var user = {};
user.codeCampType = "svcc";
app.constant('user', user);
angular.bootstrap(document, ['myApp']);
});
Note Because we're bootstrapping the app, you'll need to get rid of the ng-app directive on your view.
Here's a working fiddle
You could set it in a run() block that will get executed during bootstrapping:
baseApp.run(function($rootScope) {
$rootScope.codeCampType = response.data;
});
angular.bootstrap(document, ['baseApp']);
I don't think you can use the injector because the scope isn't created before bootstrapping. A config() block might work as well that would let you inject the data where you needed it.
I have defined one controller, and apply it to 2 views with small differences.
Angular code:
app.controller('MyCtrl', function($scope) {
$scope.canSave = false;
$scope.demo = {
files : [{
filename: 'aaa.html',
source: '<div>aaa</div>'
}, {
filename: 'bbb.html',
source: '<div>bbb</div>'
}]
}
$scope.newFile = function(file) {
$scope.demo.files.push(file);
}
$scope.$watch("demo.files", function(val) {
$scope.canSave = true;
}, true);
});
View 1:
<div ng-controller="MyCtrl"></div>
View 2:
<div ng-controller="MyCtrl"></div>
The sample code is very simple, but there are a lot of code and logic in my real project.
The View 1 and 2 have almost the same features, only with a few differences, but I do need to write some code for each of them in the controller.
I don't want to create 2 different controllers for them, because they have most of same logic. I don't want to move the logic to a service to share it between the 2 controllers, because the logic is not that common to be a service.
Is there any other way to do it?
Under the given conditions I might be doing something like
function MyCommonCtrl(type){
return function($scope, $http) {
$scope.x = 5;
if(type = 't1'){
$scope.domore = function(){
}
}
....
....
}
}
angular.module('ng').controller('Type1Ctrl', ['$scope', '$http', MyCommonCtrl('t1')]);
angular.module('ng').controller('Type2Ctrl', ['$scope', '$http', MyCommonCtrl('t2')]);
Then
<div ng-controller="Type1Ctrl"></div>
and
<div ng-controller="Type2Ctrl"></div>
I don't know your specific set-up but your 2 controllers could inherit from a common ancestor.
Type1Ctrl.prototype = new MyCtrl();
Type1Ctrl.prototype.constructor = Type1Ctrl;
function Type1Ctrl() {
// constructor stuff goes here
}
Type1Ctrl.prototype.setScope = function() {
// setScope
};
Type2Ctrl.prototype = new MyCtrl();
Type2Ctrl.prototype.constructor = Type2Ctrl;
function Type2Ctrl() {
// constructor stuff goes here
}
Type2Ctrl.prototype.setScope = function() {
// setScope
};
I also faced similar problem and scope inheritance solved my problem.
I wanted to "reuse" a controller to inherit common state/model ($scope) and functionality (controller functions attached to $scope)
As described in the "Scope Inheritance Example" I attach parent controller to an outer DOM element and child controller to the inner. Scope and functions of parent controller "merge" seamlessly into the child one.
Here is another option. Slightly modified from this blog post
app.factory('ParentCtrl',function(){
$scope.parentVar = 'I am from the parent'
};
});
app.controller('ChildCtrl', function($scope, $injector, ParentCtrl) {
$injector.invoke(ParentCtrl, this, {$scope: $scope});
});
here is a plunker