Ionic: [$injector:cdep] Circular dependency found - angularjs

That error occur in a .factory function. In my project I have 2 .factory that both used each other. Below is my controller:
.controller('MyCtrl', function($scope, Factory_A) {
$scope.do = function(){
Factory_A.do_A();
};
})
Scenario 1:
.factory('Factory_A', function(Factory_B){
var value_A = 1;
return{
do_A: function(){
if(value_A == 1){
value_A++;
Factory_B.do_B();
}
else{
return "Success"
}
}
}
})
.factory('Factory_B', function(Factory_A){
var value_B = 0;
var do_B_1 = function(){
Factory_A.do_A();
};
return{
do_B: function(){
value_B++;
if(value_B > 0){
do_B_1();
}
return true;
}
}
});
Error: [$injector:cdep] Circular dependency found: Factory_A <- Factory_B <- Factory_A
Scenario 2:
.factory('Factory_A', function(){
var value_A = 1;
return{
do_A: function(){
if(value_A == 1){
value_A++;
Factory_B.do_B();
}
else{
return "Success"
}
}
}
})
.factory('Factory_B', function(Factory_A){
var value_B = 0;
var do_B_1 = function(){
Factory_A.do_A();
};
return{
do_B: function(){
value_B++;
if(value_B > 0){
do_B_1();
}
return true;
}
}
});
Error: Factory_B is not defined
Scenario 3:
.factory('Factory_A', function(Factory_B){
var value_A = 1;
return{
do_A: function(){
if(value_A == 1){
value_A++;
Factory_B.do_B();
}
else{
return "Success"
}
}
}
})
.factory('Factory_B', function(){
var value_B = 0;
var do_B_1 = function(){
Factory_A.do_A();
};
return{
do_B: function(){
value_B++;
if(value_B > 0){
do_B_1();
}
return true;
}
}
});
Error: Factory_A is not defined
Here is the demo for Scenario 3 shows how I simulate my project flow using a factory. Thanks.

use $injector to inject dependency runtime on self or circular dependency.
Full example code
Change you js in your demo page
angular.module('ionicApp', ['ionic'])
.controller('MyCtrl', function($scope, Factory_A) {
$scope.do = function(){
Factory_A.do_A();
};
})
.factory('Factory_A', function($injector){
var value_A = 1;
return{
do_A: function(){
if(value_A == 1){
value_A++;
var Factory_B = $injector.get('Factory_B');
Factory_B.do_B();
}
else{
return "Success"
}
}
}
})
.factory('Factory_B', function($injector){
var value_B = 0;
var do_B_1 = function(){
var Factory_A = $injector.get('Factory_A');
Factory_A.do_A();
};
return{
do_B: function(){
value_B++;
if(value_B > 0){
do_B_1();
}
return true;
}
}
});

I assume you're asking how to avoid the circular dependency.
In cases like these you might inject the $injector instead. And then when calling the methods inside each factory, you inject them at that time or cache them after the first use.
.factory( 'FactoryA', function($injector){
return {
funcFoo: function(){
var fb = $injecto.get('FactoryB')
fb.funcBar()
}
}
})
.factory( 'FactoryB', function($injector){
return {
funcFoo: function(){
var fa = $injecto.get('FactoryA')
fa.funcFoo()
}
}
})

Related

One factory accessed by diff. controllers but one at a time angularjs

var app=angular.module('myApp',[]);
app.controller('FCtrl',['$scope','mockFactory',function($scope,mockFactory){
$scope.showPerson = function(){
mockFactory.fetchJson($scope.valueJson)
.then(function(){
$scope.persons = mockFactory.array;
})
}
$scope.delPerson = function(i){
mockFactory.delete(i);
}
$scope.addNamePerson = function() {
mockFactory.ADD($scope.valueFirst);
};
$scope.showConsolePerson= function(){
console.log(JSON.stringify(mockFactory.array));
}
}]);
app.controller('SCtrl',['$scope','mockFactory',function($scope,mockFactory){
$scope.showMovie = function(){
mockFactory.fetchJson($scope.valueJson)
.then(function(){
$scope.movies = mockFactory.array;
})
}
$scope.delMovie = function(i){
mockFactory.delete(i);
}
$scope.addNameMovie = function() {
mockFactory.ADD($scope.valueSecond);
};
$scope.showConsoleMovie= function(){
console.log(JSON.stringify(mockFactory.array));
}
}]);
app.controller('TCtrl',['$scope','mockFactory',function($scope,mockFactory){
$scope.showPlace = function(){
mockFactory.fetchJson($scope.valueJson)
.then(function(){
$scope.places = mockFactory.array;
})
}
$scope.delPlace = function(i){
mockFactory.delete(i);
}
$scope.addNamePlace = function() {
mockFactory.ADD($scope.valueThird);
};
$scope.showConsolePlace= function(){
console.log(JSON.stringify(mockFactory.array));
}
}]);
app.factory('mockFactory',['$http',function($http){
var Precord = {};
Precord.array = [];
Precord.assign = function (value) {
return $http.get('http://localhost:3000/scripts/' + value + '.json');
};
Precord.fetchJson = function(value){
return Precord.assign(value).success(function(response){
Precord.array = response.value;
})
}
Precord.delete = function(i){
Precord.array.splice(i,1);
}
Precord.ADD = function(value){
var newName = {
Name: value
};
Precord.array.push(newName);
}
return Precord;
}]);
How can an array in single factory be accessed by different controllers but one at a time such that any update in one controller must not reflect in other controllers? the precord.array is being used in all the controllers, but i want it to be isolated from other controllers while one controller is in use of it
After reviewing your code I found that you should keep a copy of array on controller level so that, If one controller update it then it will not reflect in other controllers,
I have modified your one controller and your factory, so try to implement it in other controllers also.
Try this
FCtrl
var app=angular.module('myApp',[]);
app.controller('FCtrl',['$scope','mockFactory',function($scope,mockFactory){
$scope.fCtrlJSON = [];
$scope.showPerson = function(){
mockFactory.fetchJson($scope.valueJson)
.then(function(){
$scope.persons = mockFactory.array;
$scope.fCtrlJSON = mockFactory.array;
})
}
$scope.delPerson = function(i){
mockFactory.delete($scope.fCtrlJSON,i);
}
$scope.addNamePerson = function() {
mockFactory.ADD($scope.fCtrlJSON,$scope.valueFirst);
};
$scope.showConsolePerson= function(){
console.log(JSON.stringify($scope.fCtrlJSON));
}
}]);
mockFactory
app.factory('mockFactory',['$http',function($http){
var Precord = {};
Precord.array = [];
Precord.assign = function (value) {
return $http.get('http://localhost:3000/scripts/' + value + '.json');
};
Precord.fetchJson = function(value){
return Precord.assign(value).success(function(response){
Precord.array = response.value;
})
}
Precord.delete = function(arrayData, i){
arrayData.splice(i,1);
}
Precord.ADD = function(arrayData, value){
var newName = {
Name: value
};
arrayData.push(newName);
}
return Precord;
}]);

Return value from Angular Service

Hi I do not understand why always I get empty array from this service when is invoked by my controller
angular
.module('dimecuba.services', [])
.factory('Contacts', function($cordovaContacts, $ionicPlatform) {
var contactsFound = [];
var contacts = {
all:function(){
var options = {};
options.multiple = true;
options.filter = "";
//options.fields = ['displayName'];
options.hasPhoneNumber = true;
$ionicPlatform.ready(function(){
$cordovaContacts.find(options).then(
function(allContacts){
angular.forEach(allContacts, function(contact, key) {
contactsFound.push(contact);
});
console.log("Contacts Found:" + JSON.stringify(contactsFound));
return contactsFound;
},
function(contactError){
console.log('Error');
}
);
});
}
}; //end contacts
console.log("Contacts:"+JSON.stringify(contacts));
return contacts;
});
Use return to chain promises. A return statement needs to be included at each level of nesting.
app.factory('Contacts', function($cordovaContacts, $ionicPlatform) {
var contacts = {
all:function(){
var options = {};
options.multiple = true;
options.filter = "";
options.hasPhoneNumber = true;
//return promise
return $ionicPlatform.ready().then(function() {
//return promise to chain
return $cordovaContacts.find(options)
}).then(function(allContacts){
var contactsFound = [];
angular.forEach(allContacts, function(contact, key) {
contactsFound.push(contact);
});
//return to chain data
return contactsFound;
}).catch(function(contactError){
console.log('Error');
//throw to chain error
throw contactError;
});
}
}; //end contacts
return contacts;
});
In the controller, use the promise returned.
app.controller("myCtrl", function($scope,Contacts) {
var contactsPromise = Contacts.all();
contactsPromise.then( function(contactsFound) {
$scope.contactsFound = contactsFound;
});
});

Another way to inject in Angular

I have a template and I'm changing the way to Inject dipendecies. It has the 'classic' way to inject. I want to replace it with the compact form. I can't do these line of code. I've tried but I can't understand the struscture. Because I am accustomed in the other form. Can somebody help me?
(function() {
'use strict';
angular.module('app.data')
.factory('postResource', postResource)
.factory('postsUtils', postsUtils);
postResource.$inject = ['$resource'];
function postResource($resource) {
return $resource('/api/posts/:id', {id: '#id'}, {
update: {
method: 'PUT'
}
});
}
postsUtils.$inject = ['postResource'];
function postsUtils(postResource) {
function postsDuringInterval(posts, days) {
var today = new Date();
var interval = 86400000 * days;
var postsDuringInterval = [];
posts.forEach(function(post) {
var postDate = new Date(post.date);
today - postDate < interval && postsDuringInterval.push(post);
});
return postsDuringInterval;
}
function recent(posts, postsNum) {
posts.sort(function(a, b) {
if (a.date < b.date) return 1;
else if (a.date == b.date) return 0;
else return -1;
});
return posts.slice(0, postsNum || 1);
}
function lastEdited(posts) {
var lastEdited = posts[0];
posts.forEach(function(post) {
lastEdited = lastEdited.date < post.date ? lastEdited : post;
});
return lastEdited;
}
return {
postsDuringInterval: postsDuringInterval,
lastEdited: lastEdited,
recent: recent
}
}
})();
Here is an example of how you would inject dependencies.
var app = angular.module('myApp',
['ngRoute', 'ngSanitize', 'ui.bootstrap', 'angular-flexslider',
'ng-backstretch', 'angular-parallax', 'fitVids', 'wu.masonry', 'timer',
'uiGmapgoogle-maps', 'ngProgress']);
An example of a controller that has a service injected into it.
app.controller('ContactController',['$scope','contactService',
function($scope, contactService) {
var self = this;
self.contact = {id:null, name:"",lastName:"",email:"",subject:"",message:""};
this.submit = function(){
contactService.submit(self.contact);
self.contact = {id:null, name:"",lastName:"",email:"",subject:"",message:""};
};
}]);
The factory:
app.factory('contactService',['$http','$q', function($http,$q){
return {
submit: function (contact) {
return $http.post('/sendForm/', contact)
.then(
function (response) {
return response;
},
function (errResponse) {
console.error("Error while submitting form" + errResponse);
return $q.reject(errResponse);
}
)
}
}
}]);
I think this is the way you were referring too. Hope this helps.

Splitting a controller in 2 different files. Angular

I am getting a headache trying to do this, I have a very big controller which I need to split, this controller has 177 lines already so I need 2 controllers. Once I try to split it my app breaks down.
Maybe you could help by giving suggestions on how to start and what I have to evaluate first. I am new to Angular and I thought it would be easier.
'use strict';
angular.module('capilleira.clickAndGambleMobile.controllers')
.controller('LinesController', function($scope, $timeout, $state,
$stateParams, $ionicLoading, $rootScope, LinesFactory, BetSlipFactory) {
$scope.picksCount = false;
var validateSanitizeLineParams = function() {
var lineParamsOk = false;
if (validator.isAlphanumeric($stateParams.customerId) &&
validator.isNumeric($stateParams.familyGameId) &&
validator.isNumeric($stateParams.games) &&
validator.isNumeric($stateParams.top) &&
validator.isNumeric($stateParams.sports) &&
validator.isLength($stateParams.leagues.trim(), 1) &&
validator.isAscii($stateParams.leagues.trim()) &&
validator.isLength($stateParams.periods.trim(), 1) &&
validator.isAscii($stateParams.periods.trim())) {
lineParamsOk = true;
_.each($stateParams.periods.split(','), function(periodId) {
if (!validator.isAlpha(periodId)) {
lineParamsOk = false;
}
});
_.each($stateParams.leagues.split(','), function(leagueId) {
if (!validator.isNumeric(leagueId)) {
lineParamsOk = false;
}
});
}
return lineParamsOk;
};
$scope.lineItems = [];
$rootScope.spinnerTitle = 'Loading lines';
$ionicLoading.show({
templateUrl: 'templates/loaders.html',
scope: $rootScope
});
$scope.doRefresh = function() {
if (validateSanitizeLineParams()) {
LinesFactory.getLines($stateParams).then(function(linesPerLeague) {
_.each(linesPerLeague, function(linesPerParent) {
_.each(linesPerParent, function(lines) {
_.each(lines, function(line) {
_.each(line.rows, function(row) {
if (!row.noSpread) {
line.displaySpreadButton = true;
}
if (!row.noTotal) {
line.displayTotalButton = true;
}
if (!row.noMoneyLine) {
line.displayMoneyLineButton = true;
}
});
if (line.displaySpreadButton) {
line.displaySpread = true;
} else if (line.displayTotalButton) {
line.displayTotal = true;
} else if (line.displayMoneyLineButton) {
line.displayMoneyLine = true;
}
});
});
});
$scope.lineItems = linesPerLeague;
if (!$scope.lineItems[0].length) {
$state.go('app.noLines');
}
$ionicLoading.hide();
$scope.addLineSelections();
}, function(err) {
console.log(err);
$rootScope.spinnerTitle = 'Wrong Params';
$ionicLoading.show({
templateUrl: 'templates/loaders.html',
scope: $rootScope
});
$timeout(function() {
$ionicLoading.hide();
$state.go('app.login');
}, 1500);
});
}else {
$rootScope.spinnerTitle = 'Wrong Params';
$ionicLoading.show({
templateUrl: 'templates/loaders.html',
scope: $rootScope
});
$timeout(function() {
$ionicLoading.hide();
$state.go('app.login');
}, 1500);
}
$scope.$broadcast('scroll.refreshComplete');
};
$scope.doRefresh();
$scope.showLine = function(lineType, line) {
switch (lineType) {
case 'spread':
line.displayTotal = false;
line.displayMoneyLine = false;
line.displaySpread = false;
$timeout(function() {
line.displaySpread = true;
}, 50);
break;
case 'total':
line.displaySpread = false;
line.displayMoneyLine = false;
line.displayTotal = false;
$timeout(function() {
line.displayTotal = true;
}, 50);
break;
case 'moneyline':
line.displaySpread = false;
line.displayTotal = false;
line.displayMoneyLine = false;
$timeout(function() {
line.displayMoneyLine = true;
}, 50);
break;
}
};
$scope.addLineToBetSlip = function(line, row, type) {
var spreadSelected = (row.spreadSelected && type === 'spread'),
totalSelected = (row.totalSelected && type === 'total'),
moneyLineSelected = (row.moneyLineSelected && type === 'moneyline');
if (spreadSelected || totalSelected || moneyLineSelected) {
BetSlipFactory.remove(line, row, type);
}else {
BetSlipFactory.add(line, row, type);
}
$scope.picksCount = !$scope.picksCount;
};
$scope.addLineSelections = function() {
BetSlipFactory.getBetSlip().then(function(betSlip) {
var flattenLines = _.flatten($scope.lineItems),
lineFound, row;
_.each(betSlip, function(slip) {
lineFound = _.find(flattenLines, function(line) {
return line.gameId === slip.gameId &&
line.part === slip.part &&
line.lineTypeName === slip.lineTypeName;
});
if (lineFound) {
row = _.find(lineFound.rows, function(row) {
return row.nss === slip.nss;
});
if (row) {
switch (slip.type) {
case 'spread':
row.spreadSelected = true;
break;
case 'total':
row.totalSelected = true;
break;
case 'moneyline':
row.moneyLineSelected = true;
break;
}
}
}
});
});
};
});
This works as expected.
var app = angular.module('plunker', []);
var FatCtrl1 = function($scope){
$scope.stuff1 = this.hello;
};
var FatCtrl2 = function($scope){
$scope.stuff2 = "World";
};
app.controller('FatCtrl', function($scope) {
this.hello = "Hello";
FatCtrl1.apply(this, arguments);
FatCtrl2.apply(this, arguments);
});
Beware of closures. Variables within FatCtrl are not available in the partitions. 'this' is used to share data.

Creating jasmine unit test for angular directive

The below directive checks/loads the template with value "pass, fail, required".
The conditions are
if(parent value == required){
1. if the value is true --> $scope.msg = "fail" --> loads the template with {{msg}} value
2. if the value is false --> $scope.msg = "pass" --> loads the template with {{msg}} value
}
In detail,
loading Template: [showerror.html]
<div>{{msg}}</div>
Directive Call:
<div show-error="obj"></div>
(obj contains as obj.error and obj.required )
Directive:
angular.module("dsf").directive('showError', [
function () {
'use strict';
return {
scope: {
obj: '=showError'
},
link: function ($scope) {
$scope.showError = {};
$scope.msg = "";
function setTemplate(filename) {
$scope.showError.template = 'app/path/' + filename + '.html';
}
$scope.$watch(function () {
if ($scope.obj.required === true) {
if ($scope.obj.error === false) {
$scope.msg = "Pass";
} else if ($scope.obj.error === "required") {
$scope.msg = "Required";
} else if ($scope.obj.error === true) {
$scope.msg = "fail";
}
} else {
if ($scope.obj.error === true) {
$scope.msg = "fail";
} else if ($scope.obj.error === false) {
$scope.msg = "Pass";
}
}
setTemplate("showerror");
});
},
template: '<div ng-include="showError.template"></div>'
};
}
]);
As i am new to jasmine test, how can i write the test for this directive? any suggestions?
Ok. I have written the unit test for this directive. What is the wrong now?
describe('showError', function () {
'use strict';
var compile, $scope, element;
beforeEach(module('dsf'));
beforeEach(inject(function ($compile, $rootScope) {
element = angular.element('<div show-error="obj"></div>');
$scope = $rootScope.$new();
compile = function (obj) {
$scope.obj = obj;
$compile(element)($scope);
$scope.$digest();
};
}));
it('updates the element when obj validation changes', function () {
var obj;
obj = {};
$scope = compile(obj);
$scope.apply(function () {
obj.required = true;
obj.error = true;
});
expect($scope.obj.msg).toContain('fail');
$scope.apply(function () {
obj.required = true;
obj.error = false;
});
expect($scope.obj.msg).toContain('Pass');
$scope.apply(function () {
obj.required = true;
obj.error = "required";
});
expect($scope.obj.msg).toContain('Required');
$scope.apply(function () {
obj.required = false;
obj.error = true;
});
expect($scope.obj.msg).toContain('Pass');
$scope.apply(function () {
obj.required = false;
obj.error = false;
});
expect($scope.obj.msg).toContain('fail');
});
});
I am getting error:
undefined is not an object (evaluating $scopr.apply) error
The command you're looking for is $scope.$apply() not $scope.apply().

Resources