Sharing subtotal across controllers in Angular - angularjs

I'm trying to share a subtotal calculated in one controller with another controller. When I use the code below, I get a total is not defined error.
I'd really appreciate suggestions on how to get this working right.
I'm using this Stackoverflow post as a model.
.factory('serviceA', function() {
var serviceA = {
amount: null
};
return serviceA;
})
.controller('ProjectListCtrl', function ($scope, serviceA, Projects) {
var projectList = this;
projectList.projects = Projects;
serviceA.amount = total;
projectList.total = function () {
var total = 0;
angular.forEach(projectList.projects, function (project) {
total += project.type.cost;
});
return total; //want to use this total in other controller
};
})
.controller('PaymentFormCtrl',
function ($scope, $http, serviceA) {
$scope.serviceA = serviceA;
console.log(serviceA);
// . . .
});

You may want to use a Service in this case as services are singletons, which means that only one will be created and each controller will be using the same service, rather than them using their own factory.
.service('serviceA', function() {
var amount = 0;
return {
get: function () {
return amount;
},
set: function (value) {
amount = value;
}
};
});
In the first controller you want to assign the amount in serviceA when in the projectList.total function rather than outside of it.
.controller('ProjectListCtrl', function ($scope, serviceA, Projects) {
var projectList = this;
projectList.projects = Projects;
projectList.total = function () {
var total = 0;
angular.forEach(projectList.projects, function (project) {
total += project.type.cost;
serviceA.set(total);
});
//return total; //want to use this total in other controller
};
})
Also, serviceA is a service so you use it like an object rather than assigning it to a variable
.controller('PaymentFormCtrl',
function ($scope, $http, serviceA) {
var total = serviceA.get();
console.log(total);
});

Related

Angular: Mocking a Data Service

I'm doing unit testing for the first time and I'm trying to work out how to mock a data call from a service so I can test if the data is coming back in the correct form:
My Service
angular.module('app.core')
.factory('PeopleService', PeopleService)
function PeopleService($http, $q, $filter) {
var endpoint;
var service = {
customers: {
value: null
},
getAllCustomers: getAllCustomers,
};
return service;
function getCustomers(endpoint_) {
endpoint = endpoint_;
service.customers.value = [];
return handleFetch($http.get(endpoint));
}
function handleFetch(promise) {
return promise.then(function (resp) {
service.customers.value = service.customers.value.concat(resp.data.data);
});
}
function getAllCustomers() {
return $q.all([
getCustomers('/api/customers'),
]).then(function(responses) {
return responses[0];
});
}
}
My Controller
angular.module('app.people')
.controller('peopleCtrl', peopleCtrl);
function peopleCtrl($scope, PeopleService) {
$scope.customers = PeopleService.customers;
getCustomers();
function getCustomers() {
return PeopleService.getAllCustomers().then(function () {
return PeopleService.customers.value;
});
}
}
My Test
describe('People Service', function () {
var controller;
var customers = mockData.getMockCustomers(); // my fake customers array
beforeEach(function() {
bard.appModule('app');
bard.inject('$controller', '$q', '$rootScope', 'PeopleService');
var ps = {
getAllCustomers: function() {
return $q.when(customers);
}
};
controller = $controller('peopleCtrl', {
$scope: $rootScope,
PeopleService: ps
});
});
it('should return an array of 5 customers', function() {
$rootScope.$apply();
expect($rootScope.customers).to.have.length(5);
});
});
I've got a controller set up that when loaded talks to the People Service and gets my customers and saves the array of customers to PeopleService.customers.value. Inside my controller, I have a variable $scope.customers which is equal to PeopleService.customers.
I'm trying to mock this with my test, without hitting the API, I'm using some mock data to do this (an array of 5 customers), but not sure if I understand correctly.
Is the idea to have my mock people service return exactly what the actual people service returns? I'm kind of confused at this point. I basically want that test to check if the mock data length is equal to five.
Any help with this is appreciated. Thanks in advance!

angular js access variable in a service but same app

How can I access a property of a controller in a service?
In my case it is an array, which I want to change in my service.
My controller:
myApp.controller('MainController', function ($scope, $interval, externalDataService, AnalyzeService) {
$scope.myArray = [];
var size = 10;
var analyze = function () {
if($scope.myArray .length > size) {
AnalyzeService.doSomething();
}
};
var analyzeData = function () {
externalDataService.getData().then(function (data) {
$scope.faceReaderData.push(data);
analyze();
});
};
$interval(analyzeData , 2000);
});
My service:
myApp.service('AnalyzeService', function () {
this.doSomething = function () {
//here i want array access
}
});
You do not want to do that, as it would make your service depend on your controller, which is very undesireable. Instead, add the array as a parameter to your doSomething:
//Appending 1,2,3 to the given array
function doSomething(someArray) {
return someArray.concat(['1', '2', '3']);
}
This way, your service does not depend on your controller at all, seperating your business logic from your controller.

pass data between controllers in AngularJS dynamically [duplicate]

This question already has answers here:
Share data between AngularJS controllers
(11 answers)
Closed 2 years ago.
i have tow controller in angularjs. if one controller change data other controller display updated data. in fact first controller has a event that it occur second controller display it. for this propose i wrote a service. this service has tow function. here is my service code.
app.service('sharedData', function ($http) {
var data=[]
return {
setData: function () {
$http.get('/getData').success(function(response){
data = response;
})
},
getData: function(){
return data;
}
}
});
in first controller
app.controller("FirstController", function ($scope, $http,sharedData)
{
$scope.handleGesture = function ($event)
{
sharedData.setData();
};
});
in second controller:
app.controller("SecondController", function ($scope,sharedData) {
var data=[];
data = sharedData.getData();
}
);
in first controller setData work with out any problem but in second controller not work correctly. how to share data dynamically between tow controllers?
You are on the right track with trying to share data between controllers but you are missing some key points. The problem is that SecondController gets loaded when the app runs so it calls sharedData.getData() even though the call to setData in the firstController does not happen yet. Therefore, you will always get an empty array when you call sharedData.getData().To solve this, you must use promises which tells you when the service has data available to you. Modify your service like below:
app.service('sharedData', function ($http, $q) {
var data=[];
var deferred = $q.defer();
return {
setData: function () {
$http.get('/getData').success(function(response){
data = response;
deferred.resolve(response);
})
},
init: function(){
return deferred.promise;
},
data: data
}
})
And the secondController like this:
app.controller("SecondController", function ($scope,sharedData) {
var data=[];
sharedData.init().then(function() {
data = sharedData.data;
});
});
For more info on promises, https://docs.angularjs.org/api/ng/service/$q
You had multiple syntax problems, like service name is SharedData and you using it as SharedDataRange, the service is getting returned before the get function.
What I have done is corrected all the syntax errors and compiled into a plunkr for you to have a look. Just look at the console and I am getting the data array which was set earlier in the setter.
Javascript:
var app = angular.module('plunker', []);
app.controller("FirstController", function ($scope,sharedDateRange)
{
sharedDateRange.setData();
});
app.controller("SecondController", function ($scope,sharedDateRange) {
var data=[];
data = sharedDateRange.getData();
console.log(data);
});
app.service('sharedDateRange', function ($http) {
var data=[];
return {
setData: function () {
data = ['1','2','3'];
}
,
getData: function(){
return data;
}
}
});
Working Example
If you want to keep sharedDataRange as the variable name and service name as sharedData have a look at this example
javascript:
var app = angular.module('plunker', []);
app.controller("FirstController", ['$scope','sharedData', function ($scope,sharedDateRange)
{
sharedDateRange.setData();
}]);
app.controller("SecondController", ['$scope','sharedData', function ($scope,sharedDateRange) {
var data=[];
data = sharedDateRange.getData();
console.log(data);
}]);
app.service('sharedData', function ($http) {
var data=[];
return {
setData: function () {
data = ['1','2','3'];
}
,
getData: function(){
return data;
}
}
});
You can bind the data object on the service to your second controller.
app.service('sharedData', function ($http) {
var ret = {
data: [],
setData: function () {
$http.get('/getData').success(function(response){
data = response;
});
}
};
return ret;
});
app.controller("FirstController", function ($scope, sharedData) {
$scope.handleGesture = function () {
sharedData.setData();
};
});
app.controller("SecondController", function ($scope, sharedData) {
$scope.data = sharedData.data;
});
What you need is a singleton. The service sharedData needs to be a single instance preferably a static object having a static data member. That way you can share the data between different controllers. Here is the modified version
var app = angular.module('app', []);
app.factory('sharedData', function ($http) {
var sharedData = function()
{
this.data = [];
}
sharedData.setData = function()
{
//$http.get('/getData').success(function(response){
this.data = "dummy";
//})
}
sharedData.getData = function()
{
return this.data;
}
return sharedData;
})
.controller("FirstController", function ($scope, $http,sharedData)
{
sharedData.setData();
})
.controller("SecondController", function ($scope,sharedData) {
$scope.data=sharedData.getData();
});
I have removed the event for testing and removed the $http get for now. You can check out this link for a working demo:
http://jsfiddle.net/p8zzuju9/

Angular provider (service) for store data fetched from an api rest?

I'm using a controller to load product data into an $rootScope array. I'm using $http service and works fine, but now I have a new function which fetch the number of products to be loaded. I can't use the function cause the response is slow.
I was wondering if I could use a provider to load the number of products to fetch in the config method before the apps start. And if I could move the $rootScope array to one service. I don't understand Angular docs, they are not really useful even the tutorial at least in providers and services...
app.controller('AppController', [ '$rootScope', '$http', function ( $rootScope,$http) {
$rootScope.empty = 0;
$rootScope.products = [];
$rootScope.lastId = 0;
$rootScope.getLastID = function () {
$http.get("app_dev.php/api/products?op=getLastId").success(function (data) {
$rootScope.lastId = data.lastId;
});
};
$rootScope.getProducts = function () {
if ($rootScope.empty === 0) {
for (i = 1; i < 100; i++) {
$http.get("app_dev.php/api/product/" + i).success(function (data) {
$rootScope.products.push(data);
});
}
}
$rootScope.empty.productos = 1;
};
}
I have done this with factory and service but is not working.
app.factory('lastProduct', ['$http', function lastProductFactory($http) {
this.lastId;
var getLast = function () {
$http.get("app_dev.php/api/products?op=getLastId").success(function (data) {
lastId = data.lastId;
});
return lastId;
};
var lastProduct = getLast();
return lastProduct;
}]);
function productList($http, lastProduct) {
this.empty = 0;
this.lastId = lastProduct();
this.products = []
/*this.getLast = function () {
lastId = lastProduct();
};*/
this.getProducts = function () {
if (empty === 0) {
for (i = 1; i < lastId; i++) {
$http.get("app_dev.php/api/product/" + i).success(function (data) {
products.push(data);
});
}
}
empty = 1;
return products;
};
}
app.service('productsList', ['$http', 'lastProduct' , ProductsList]);
services are not availables during configuration time, only providers hence you can not use $http to get a value inside the configuration block, but you can use the run block,
you can do
angular.module('app',['dependencies']).
config(function(){
//configs
})
.run(function(service){
service.gerValue()
})
setting the retrieved value inside a service or inside a value is a good idea to avoid contaminate the root scope, and this way the value gets retrieved before the services are instantiated and you can inject the retrieved value as a dependency
Making that many small $http requests does not seem like a good idea. But using a factory to store an array of data to be used across controllers would look something like this. To use a factory you need to return the exposed api. (The this style is used when using a service. I suggest googling the different but I prefer factories). And if you need to alert other controllers that data has changed you can use events.
angular
.module('myApp')
.factory('myData', myData);
function myData($http, $rootScope) {
var myArray = [], lastId;
return {
set: function(data) {
$http
.get('/path/to/data')
.then(function(newData) {
myArray = newData;
$rootScope.$broadcast('GOT_DATA', myArray);
})
},
get: function() {
return myArray
},
getLast: function() {
$http
.get('/path/to/data/last')
.then(function(last) {
lastId = last;
$rootScope.$broadcast('GOT_LAST', lastId);
})
}
}
}
And then from any controller you can inject the factory and get and set the data as you see fit.
angular
.module('myApp')
.controller('MainCtrl', MainCtrl);
function MainCtrl($scope, myData) {
$scope.bindableData = myData.get(); // get default data;
$scope.$on('GOT_DATA', function(event, data) {
$scope.bindableData = data;
})
}
I hope this helps. Let me know if you have any questions.
I done this but not working . rootScope total is undefined when set method is called from some controller.
http://imgur.com/qEl5WV5
But using 10 instead rootscope total
http://imgur.com/t6wB3JZ
I could see that the rootScope total var arrive before the others...
(app_dev.php/api/productos?op=ultimaIdProductos) vs (app_dev.php/api/producto/x)
var app = angular.module('webui', [$http, $rootScope]);
app.run (function ($http, $rootScope){
$http.get("app_dev.php/api/products?op=getLastId").success(function (data) {
$rootScope.total = data.ultima;
});
});
function myData($http, $rootScope) {
var myArray = [];
return {
set: function () {
console.log($rootScope.total);
for (i = 1; i < $rootScope.total; i++) {
$http.get("app_dev.php/api/product/" + i).success(function (data) {
myArray.push(data);
})
}
},
get: function () {
return myArray;
}
}
}
app.controller('AppController', ['$http', '$rootScope', 'myData', function ($http, $rootScope, myData) {
$rootScope.productos = [];
$rootScope.getProductos = function () {
console.log($rootScope.total);
myData.set();
$rootScope.productos = myData.get();
};
}]);

Angular services

I am trying to share an id between controllers in angular
I have created a service as follows:
app.factory("idService", function() {
var id;
addId = function(id) {
id = id;
};
getId = function() {
return id;
};
});
In my controller I am trying to use this service as follows:
app.controller('photoFormController', ['$scope', '$http', 'idService' , function($scope, $http, idService) {
$scope.id = idService.getId();
}]);
I am getting an error of can't call method of undefined, obviously I am injecting the service incorrectly. Can someone help ?
EDIT:
Based on the solution below, the service no longer generates errors, however I am unable to get the id varaible back, I can see that it gets set from one controller, however it remains undefined in when retrieving :
app.factory("idService", function() {
var id;
addId = function(id) {
id = id;
console.log("added id of: " + id);
};
getId = function() {
console.log("trying to return : " + id);
return id;
};
return {
addId: addId,
getId: getId
};
});
You need to return an object inside factory. This returned object is your service instance:
app.factory("idService", function() {
var _id; //use _id instead to avoid shadowing your variable with the same name id
var addId = function(id) { //use var to avoid creating a property on the global object
_id = id;
};
var getId = function() { //use var to avoid creating a property on the global object
return _id;
};
return {
addId : addId ,
getId : getId
};
});
This is because you need to place the id within an object.
fiddle
This idea is as follows:
myApp.controller('ctrl1', function($scope, myservice) {
$scope.updateId = function() {
myservice.setId($scope.input)
};
});
myApp.controller('ctrl2', function($scope, myservice) {
$scope.data = myservice.getData();
});
myApp.service('myservice', function() {
var myservice = this;
var data = {};
myservice.setId = function(newValue) {
data.id = newValue;
};
myservice.getData = function() {
return data;
}
});
This way the data object that you get from your service sort of static, and the mutable member that you are outputting is contained within it.
You don't need to create a service in order to share some variable, just use value():
JavaScript
angular.module('app',[]).
value('share', {id: 123}).
run(['share', function(share) {
console.log(share.id); // <= 123
share.id = 345;
}]).
controller('appController', ['$scope', 'share', function($scope, share) {
$scope.id = share.id; // <= 345
}]);
Plunker: http://plnkr.co/edit/m55hmFgBvi1rwVsYMeKU?p=preview
Example of service which store data between controllers:
'use strict';
angular
.module('app')
.factory('youService', youService);
youService.$inject = ['$rootScope'];
function youService($rootScope) {
var service = {};
var questions = [];
// Data
function getData() {
return questions;
};
function setData(newQuestion) {
questions.push(newQuestion);
};
function resetData() {
questions = {};
};
// Services
service.getData = getData;
service.setData = setData;
service.resetData = resetData;
return service;
};
In controller:
angular.module('app')
.controller('yourController', yourController);
stepFourController.$inject = ['$scope', 'youService'];
function yourController($scope, youService){
// Get saved data from service
$scope.model = youService.getData();
// Set data to service
youService.setData($scope.model);
// Reset data in service
youService.resetData();
};
Best way to share data between controller is through services or factories
The service can be written as:
var myApp = angular.module('myApp', []);
myApp.service('shareId', [function () {
var shareIdService = this;
shareIdService.id = '';
shareIdService.setId = function (id) {
shareIdService.id = id;
}
shareIdService.getId = function () {
return shareIdService.id;
}
}]);
or factory may be written like as
myApp.factory('shareId', [function () {
var id = '';
return {
setId:function (id){
id=id;
},
getId:function (){
return id;
}
}
}])

Resources