I try to use ng-notifications-bar module, I have code like this:
angular.module('app', [
uiRouter,
Common.name,
Components.name,
angularMaterial,
'ngTable',
'gridster',
'ngNotificationsBar'
])
.factory('$exceptionHandler', ['notifications', function(notifications) {
return function(exception, cause) {
notifications.showError({message: exception});
};
}]);
but got error:
[$injector:cdep] Circular dependency found: $rootScope <- notifications <- $exceptionHandler <- $rootScope <- $timeout <- $$rAF <- $mdGesture
I've tried to modify the library to use $injector to get $timeout and $rootScope but that didn't help also tried to use $injector to get notifications in $exceptionHandler factory but got the same error.
Pretty poor design from angular it looks on this one. You can't inject $rootScope in any form into $exceptionHandler due to the dependency.
You can use $injector to get around these kinds of (out-of-your-hands) dependency problems, you just need to make sure that the injected module is used inside the return function to ensure that at the time of calling .get() the dependent module has actually loaded. For example:
// won't not be available here
var rootScope = $injector.get('$rootScope');
return function(exception, cause) {
// will be available here
var rootScope = $injector.get('$rootScope');
};
This is because $injector is used to grab the dependency at run time.
There is a nice clean object-oriented design to avoid circular dependencies: use the dependency inversion principle. Create a generic service to which you can attach handlers, and set it up from a run block. Basically all the other solutions suggest something similar, but with using global variables outside of angular, or bypassing the automatic dependency injection.
angular.module("App", [])
.factory("notifications", function($rootScope) {
$rootScope.notifications = [];
function showMessage(msg) {
$rootScope.notifications.push(msg);
}
return { showMessage };
})
.factory("$exceptionHandler", function(MyExceptionService) {
return function(e, cause) {
MyExceptionService.fire(e, cause);
};
})
.factory("MyExceptionService", function() {
const handlers = [];
return {
addHandler(h) { handlers.push(h); },
fire(e, cause) { handlers.forEach(h => { h(e, cause); }) }
};
})
.controller("MyCtrl", function($scope) {
$scope.clicked = () => {
throw new Error("Error made");
};
})
.run(function(MyExceptionService, notifications) {
MyExceptionService.addHandler(err => {
notifications.showMessage({ message: err.message });
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.js"></script>
<div ng-app="App" ng-controller="MyCtrl">
<button ng-click="clicked()">Make Error</button>
<div>Errors:</div>
<div ng-repeat="item in notifications">{{item}}</div>
</div>
For comparison, here is the wrong one (with the circular dependency):
angular.module("App", [])
.factory("notifications", function($rootScope) {
$rootScope.notifications = [];
function showMessage(msg) {
$rootScope.notifications.push(msg);
}
return { showMessage };
})
.factory("$exceptionHandler", function(notifications) {
return function(e, cause) {
notifications.showMessage({ message: err.message });
};
})
.controller("MyCtrl", function($scope) {
$scope.clicked = () => {
throw new Error("Error made");
};
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.js"></script>
<div ng-app="App" ng-controller="MyCtrl">
<button ng-click="clicked()">Make Error</button>
<div>Errors:</div>
<div ng-repeat="item in notifications">{{item}}</div>
</div>
Related
I actually hate to be that guy, but I've been sitting with this
problem for some days now. I have these three files as a part of a
larger angularjs application. I can not get even this rudimentary test
to pass (or even work). I've been comparing files within the project,
I've read on-line (tried all those ways people have suggested). I have
even written the files from scratch a few times. I'm probably not able
to see my error anymore. I guess this is easier to spot (right away)
for a back-seat driver.
I'd be most appreciative for any help.
The output from gulp/karma
PhantomJS 2.1.1 (Linux 0.0.0) SiteDescriptionService the service should be defined FAILED
Error: [$injector:unpr] Unknown provider: SiteDescriptionServiceProvider <- SiteDescriptionService
http://errors.angularjs.org/1.5.8/$injector/unpr?p0=SiteDescriptionServiceProvider%20%3C-%20SiteDescriptionService (line 4511)
bower_components/angular/angular.js:4511:86
getService#bower_components/angular/angular.js:4664:46
bower_components/angular/angular.js:4516:48
getService#bower_components/angular/angular.js:4664:46
injectionArgs#bower_components/angular/angular.js:4688:68
invoke#bower_components/angular/angular.js:4710:31
workFn#bower_components/angular-mocks/angular-mocks.js:3085:26
loaded#http://localhost:8080/context.js:151:17
inject#bower_components/angular-mocks/angular-mocks.js:3051:28
app/service/sitedescriptor-service-test.js:10:19
app/service/sitedescriptor-service-test.js:4:13
global code#app/service/sitedescriptor-service-test.js:1:9
Expected undefined to be truthy.
app/service/sitedescriptor-service-test.js:17:32
loaded#http://localhost:8080/context.js:151:17
The module declaration
(function(){
'use strict';
angular.module('application.service', []);
})();
The service itself
(function () {
angular.module('application.service')
.service('SiteDescriptorService',
['$http', '$q', function ($http, $q) {
var lastRequestFailed = true,
promise,
items = [];
return {
name: 'SiteDescriptorService',
getItems: function () {
if (!promise || lastRequestFailed) {
promise = $http.get('site.json').then(
function (response) {
lastRequestFailed = false;
items = response.data;
return items;
}, function (response) { // error
lastRequestFailed = true;
return $q.reject(response);
});
}
return promise;
}
};
}]
);
})();
and the test
describe('SiteDescriptionService', function() {
'use strict';
describe('the service', function() {
var service, httpBackend;
beforeEach(module('application.service'));
beforeEach(inject(function(_SiteDescriptionService_, $httpBackend) {
service = _SiteDescriptionService_;
httpBackend = $httpBackend;
console.log(service);
}));
it('should be defined', function() {
expect(service).toBeTruthy();
});
});
});
Cheers
Mats
Looks like you just use incorrect name when injecting dependency, should be 'SiteDescriptorService' and not 'SiteDescriptionService'
How can I run the working code in createAdmobBanner function in another controller?
angular.module('starter', ['ionic', 'starter.controllers'])
.run(function ($ionicPlatform) {
$ionicPlatform.ready(function () {
var admobid = {};
if (/(android)/i.test(navigator.userAgent)) {
admobid = {
banner: 'ca-app-pub-3815248714018431/123456789'
};
}
function createAdmobBanner() {
AdMob.createBanner({
adId: admobid.banner
adSize: 'SMART_BANNER',
position: 8
});
}
createAdmobBanner();
});
})
I got createAdmobBanner is not defined if I simply do createAdmobBanner() in my controllers. I tried $rootScope but the plugin doesn't seem work with that.
You need to add it into a service or attached in on $rootScope,
$rootScope solution - faster to implement but "dirty"
.run(function($ionicPlatform,$rootScope) { //add $rootScope dependency injection
$rootScope.createAdmobBanner = function(){
AdMob.createBanner( { adId:admobid.banner
adSize: 'SMART_BANNER',
position:8
});
}
$rootScope.createAdmobBanner()
into your controllers, add the dependency $rootScope and call your function $rootScope.createAdmobBanner
Service Solution - cleaner & reusable
Create a new service that has your function
Inject your service into run
call your service function into run
inject your service into controllers
call your service function into controllers
I just found this link here. Give it a try. The important code looks like this:
var admobApp = angular.module('myapp', ['ionic'])
.run(function($ionicPlatform, $ionicPopup) {
$ionicPlatform.ready(function() {
if(window.plugins && window.plugins.AdMob) {
var admob_key = device.platform == "Android" ? "ANDROID_PUBLISHER_KEY" : "IOS_PUBLISHER_KEY";
var admob = window.plugins.AdMob;
admob.createBannerView(
{
'publisherId': admob_key,
'adSize': admob.AD_SIZE.BANNER,
'bannerAtTop': false
},
function() {
admob.requestAd(
{ 'isTesting': false },
function() {
admob.showAd(true);
},
function() { console.log('failed to request ad'); }
);
},
function() { console.log('failed to create banner view'); }
);
}
});
});
The admob stuff is within $ionicPlatform.ready(function() { and is defined like this var admob = window.plugins.AdMob;
Does that help?
Try to define external angular service/factory and provide this service to any controller you need using dependency injection.
This is a good practice to share common logic or data in this way.
EDIT:
angular.module('starter', ['ionic', 'starter.controllers']);
angular.module('starter').factory('bannerFactory',function(){
return {
createAdmobBanner: function(){
window.plugins.AdMob.createBanner({ adId:admobid.banner
adSize: 'SMART_BANNER',
position:8
});
}
}
});
angular.module('starter').controller('anyController',['bannerFactory', function(bannerFactory){
bannerFactory.createAdmobBanner();
}]);
angular.module('starter').run(function ($ionicPlatform,bannerFactory) {
$ionicPlatform.ready(function () {
bannerFactory.createAdmobBanner();
});
});
I have problem related AngularJS dependency injection and timing between them. Here is my code and error
var module = angular.module('Demo', []);
module.factory('demo', function () {
return {
data: {},
};
});
module.provider('foo', ['demo', function(demo) {
console.log(demo);
this.$get = function() {
};
}]);
Error:
Uncaught Error: [$injector:modulerr] Failed to instantiate module Demo due to:
Error: [$injector:unpr] Unknown provider: demo
But if I add setTimeout on last definition everything works fine, but its hacking code it shouldn't be like this.
var module = angular.module('Demo', []);
module.factory('demo', function () {
return {
data: {},
};
});
setTimeout(function(){
module.provider('foo', ['demo', function(demo) {
console.log(demo);
this.$get = function() {
};
}]);
});
Here is problem on fiddle:
http://jsfiddle.net/zcf7rb4s/1/
You cannot add demo as a dependency there because it does not yet exist. That's the way the $injector works. What you can do is list demo as a dependency in the $get function of the provider. That's going to be executed by the $injector after all providers have been defined.
Check this:
<div ng-app="Demo">
<div ng-controller="test">{{x}}</div>
</div>
And the definitions:
var module = angular.module('Demo', []);
module.factory('demo', function () {
return {
data: {x: 'x'},
};
});
module.provider('foo', function() {
this.$get = function(demo) {
return {
demo: demo
};
};
});
module.controller('test', ['$scope', 'foo', function($scope, foo) {
$scope.x = foo.demo.data.x;
}]);
The code inside the factory and provider is run at "step 1".
Then, in "step 2" AngularJS binds the controller. It first uses $injector to inject the dependencies (that have been previously defined in "step 1"). So in practice your $timeout "emulates" this behavior, that's why it works. But it's wrong, that's not the way you are supposed to use them.
Inject into the provider like this instead:
module.provider('foo', function() {
this.$get = ['demo', function(demo) {
console.log(demo);
}];
});
for my web page I have several angular apps. For those apps I want to create a global error handler which tracks errors with codes 500, 401 and so on and displays them as alerts.
Here is what I have so far:
I've created a global error handler module which I then inject in my apps
angular.module('globalErrorHandlerModule', [])
.factory('myHttpInterceptor', ['$rootScope', '$q', function ($rootScope, $q) {
return {
'responseError': function (rejection) {
if(rejection.status == 500){
// show error
}
return $q.reject(rejection);
}
};
}])
.config(function ($httpProvider) {
$httpProvider.interceptors.push('myHttpInterceptor');
});
angular.module('myApp', ['globalErrorHandlerModule'])
Now what I'm struggling with is actually displaying the error in an alert. What's the best way to do this? I've tried creating a separate error app and injecting the error module and share a data factory in between, but the data never gets updated in the app. Something like this:
angular.module('globalErrorHandlerModule', [])
.factory('myHttpInterceptor', ['$rootScope', '$q', 'Data', function ($rootScope, $q, Data) {
return {
'responseError': function (rejection) {
if(rejection.status == 500){
// set error
Data.error.message = '500 error';
}
return $q.reject(rejection);
}
};
}])
.factory('Data', function () {
var _error = {
message: "init"
};
return {
error: _error
};
})
.config(function ($httpProvider) {
$httpProvider.interceptors.push('myHttpInterceptor');
});
angular.module('globalErrorHandlerApp', ['globalErrorHandlerModule'])
.controller('GlobalErrorCtrl', function ($scope, Data) {
$scope.test = Data.error.message;
});
And then displaying the error as follows:
<div ng-controller="GlobalErrorCtrl">
Error {{test}}
</div>
But as mentioned I only see my initial value, and no updates to the error message. I've also tried broadcasting but that didn't work either. I'm sure there's a better way to implement something like this, I just haven't found it yet. Thanks for any tips pointing me in the right direction.
try with this
angular.module('globalErrorHandlerApp', ['globalErrorHandlerModule'])
.controller('GlobalErrorCtrl', function ($scope, Data) {
$scope.test = Data.error;
});
its a better idea watch an object than a string.
let me know if help you
<div ng-controller="GlobalErrorCtrl">
Error <span> {{test.message}} </span>
</div>
When I return from my service call I seem unable to update my view. Why does 'not broken' never get out putted to the console?
the services returns [{test: 'service workies'}]
app.controller('foo-controller', ['fooService','$scope', function (fooService,$scope) {
var ctrl = this;
ctrl.Results = [{ test: 'no workies' }];
ctrl.Search = function () {
fooService.GetFoos().then(function (result) {
console.log('test');
console.log(ctrl.Results);
ctrl.Results = result;
console.log(ctrl.Results);
$scope.$apply(function () {
console.log('not broken');//never fires!!
ctrl.Results = [{test : 'workies' }]
});
});
};
return ctrl;
}]);
app.directive('fooLogo', function () {
return {
restrict: 'E',
templateUrl: './App/Templates/foo.html',
controller: 'foo-controller',
controllerAs: 'f'
};
});
edit foo service
.service('fooService', ['$http', function ($http) {
return $http.get("https://www.googleapis.com/books/v1/volumes?q=harry+potter").then(
function(result){ return [{ test: 'service workies'}]},
function(error) { return [{test: 'service call no workies'}] );
I see a few issues in your code. I don't see anywhere inside fooService where GetFoos() is declared, so that's one issue. Try the following:
app.controller('MainCtrl', ['$scope', 'BookQueryService',
function($scope, BookQueryService) {
$scope.search = function() {
BookQueryService.getBooks().then(function(data) {
$scope.books = data.data.items;
});
};
// call immediately for the sake of this example
$scope.search();
}
]);
app.service('BookQueryService', ['$http',
function($http) {
var service = {};
service.getBooks = function() {
return $http.get("https://www.googleapis.com/books/v1/volumes?q=harry+potter");
};
return service;
}
]);
app.directive('myBookList', function() {
return {
restrict: 'E',
templateUrl: 'BookList.html',
controller: 'MainCtrl'
}
});
With the following html:
<body>
<my-book-list></my-book-list>
</body>
And the following directive template:
<div>
<ul>
<li data-ng-repeat="book in books">
{{book.volumeInfo.title}}
</li>
</ul>
</div>
Here's a plunker with a working example:
http://plnkr.co/edit/KJPUWj0ghDi1tyojHNzI?p=preview
Is anything inside the fooService.GetFoos().then(function(result){...}) being run? If the code you posted is all there is for fooService, then it looks like there is no .GetFoos method & therefore nothing inside the following .then would get run.
Try adding a .error after the original .then that is chained onto fooService.GetFoos:
fooService.GetFoos().then(function (result) {
// your code
}).error(function (data, status){
console.log("Error!\t", status);
};
This will help you figure out what exactly is going on. Whenever your using any sort of promise, make sure you have a .catch or .error — they can save you a lot of trouble when debugging. Check out angular's $http documentation for more details.
Additionally, it looks like the original call to $scope.$apply() is unnecessary. You would only use that if you want to run a function outside of angular, or if you manually want to trigger the digest cycle (if that were the case calling $scope.$digest() explicitly would be much more appropriate than $scope.$apply.
Check out this blog post about when to use $scope.$apply and the $scope.$apply documentation page for more info