I am trying to share data between controllers - my side nav controller with a list of tables and my main controller witch controls the table itself..
my service is pretty simple:
.factory('TableData', ['$resource',
function($resource) {
return $resource('api/db/:table', {table: '#table'}, {
'save': {method:'POST'},
'update': { method:'PUT' },
'delete':{method:'DELETE'},
'list': {method: 'GET', isArray: true },
'getTable': {method: 'GET', isArray: true }
});
}])
and the controllers:
.controller('SideNavController', function ($scope, $http, $state, $auth, Account, $stateParams, Tables, $rootScope, TableData) {
TableData.list().$promise.then(function(tables){
$scope.tables = tables;
});
$scope.getTable = function(table){
TableData.getTable({table:table}).$promise.then(function(table){
$scope.selectedTable = table;
});
}
});
and the main controller:
.controller('ManagerCtrl', function($scope, $auth, Account, $http, $rootScope, ParamData, UserData, DocParamData, DocTypeData, ParamTypeData, ParamValueData, SysParamValuesData, TableData ) {
$scope.selectedTable = TableData.getTable();
})
how would i share this data? how can i pass th table parameter?
One way you can associate table with $rootScope and since it is the parent scope of all the $scope in angular therefore table can be accessible both of the controllers.
Code:
.controller('SideNavController', function ($scope, $http, $state, $auth, Account, $stateParams, Tables, $rootScope, TableData) {
TableData.list().$promise.then(function(tables){
$scope.tables = tables;
});
$scope.getTable = function(table){
TableData.getTable({table:table}).$promise.then(function(table){
$rootScope.selectedTable = table;
});
}
});
Now this $rootScope.selectedTable can be accessed in both of the controllers.
Second way, You can write one angular service where you can put getTable() method.
Code:
app.service('MyService', function($http, TableData) {
this.getTable = function(table, callback){
TableData.getTable({table:table}).$promise.then(function(table){
callback(table);
});
}
});
Now you can inject this service and call getTable() method which would pass table obj to callback.
.controller('ManagerCtrl', function($scope, $auth, Account, $http, $rootScope, ParamData, UserData, DocParamData, DocTypeData, ParamTypeData, ParamValueData, SysParamValuesData, TableData, MyService ) {
MyService.getTable(table, function(table) {
$scope.selectedTable = table;
});
})
Related
I am making an Ionic app and I am utilizing $http service to pull in a page data from a server in JSON format through an API. My factory looks like this:
app.factory('FaqHiveData', function ($http, $q, $timeout, $log, $stateParams, $ionicLoading) {
var faqdata = [];
$ionicLoading.show();
return {
all: function () {
var dfd = $q.defer();
$http.get("http://hive.squaresoftng.com/api/get/faq/")//, {cache: true}
.then(function (response) {
faqdata = response.data;
dfd.resolve(faqdata);
}).finally(function(){
$ionicLoading.hide();
});
return dfd.promise;
}
};
});
in app.js, i am using a resolve function in my app.config for each state
.state('app.faq', {
url: '/faq',
views: {
'menuContent': {
templateUrl: 'templates/faq.html',
controller: 'FaqCtrl',
resolve: {
faqdata: function (FaqHiveData) {
return FaqHiveData.all();
}
}
}
}
})
i also enable cache in my app.config
// enable http caching
$httpProvider.defaults.cache = true;
then i use the return data in my controller from the scope
app.controller('FaqCtrl', function ($scope, $stateParams, $http, $timeout, $log, faqdata, ionicMaterialInk) {
$scope.faqdata = faqdata;
//add ref to other needed value in the scope.faqdata
$scope.faqs = $scope.faqdata.faqs;
});
this works for accessing the data in my views, but i want to set this returned data to local storage in order to have access to it when the app starts or there is no connection.
I've learning to use Karma to test my angularjs app. However, not a few of my controllers use multiple services which are http requests that retrieve json which is then loaded to the page. I've been stuck because of two questions I can't answer. 1) How do I mock these services 2) what exactly should I test for my controller Question 2 I find difficult to answer because controller functions depend on services which I use in other controllers. Anyway, let me show one of my controllers and then, the "library" housing my services:
One of my controllers
angular.module('ccApp')
.controller('CountriesCtrl', ['$scope', '$routeParams', '$location','countryInfo', 'getCountries', 'countriesCache', 'getNeighbors',
'buildCountry', '$timeout', '$q',
function($scope, $routeParams, $location, countryInfo, getCountries, countriesCache, getNeighbors,
buildCountry, $timeout, $q){
getCountries.countriesObject.then(function(response){
$scope.geocountries = response.data.geonames;
},
function(response){
alert("error");
});
$scope.toCountry = function(geocountry){
getNeighbors(geocountry.geonameId)
.then(function(response){
buildCountry(geocountry, response);
var path = '/countries/'+countryInfo.name+'/capital';
$location.path(path);
}),
function(response){
alert('Error');
};
};
$scope.goHome = function(){
$location.path('/');
};
}]);
What here should I test in the controller spec?
Here's the library where the services are housed:
angular.module('library', [])
.service('countryInfo', function(){
var country = {
name: '',
pop: '',
area: '',
capital: '',
code: '',
capPop: '',
numNeigh: 0,
neighbors: []
};
return country;
})
.factory('countriesCache', ['$cacheFactory', function($cacheFactory){
return $cacheFactory('countriesCached');
}])
.factory('getCountries', ['$http', function($http){
var request = {
username: 'vman'
};
return { countriesObject : $http({
method: 'GET',
url: 'http://api.geonames.org/countryInfoJSON',
params: request
})};
}])
.factory('getCountry', ['$http', function($http){
return function(countryCode){
return $http({
method: 'GET',
url: 'http://api.geonames.org/countryInfoJSON',
params: { username: 'vman', country: countryCode }
});
};
}])
.factory('getNeighbors', ['$http', function($http){
return function(geonameId){
return $http({
method: 'GET',
url: 'http://api.geonames.org/neighboursJSON',
params: { username: 'vman', geonameId: geonameId }
});
};
}])
.factory('buildCountry', ['countryInfo', '$q', '$timeout', function(countryInfo, $q, $timeout){
return function(geocountry, response){
countryInfo.name = geocountry.countryName;
countryInfo.code = geocountry.countryCode;
countryInfo.pop = geocountry.population;
countryInfo.area = geocountry.areaInSqKm;
countryInfo.capital = geocountry.capital;
countryInfo.neighbors = response.data.geonames;
countryInfo.numNeigh = response.data.geonames.length;
};
}])
.run(['$rootScope', '$location', function($rootScope, $location) {
$rootScope.$on('$routeChangeError', function() {
$location.path('/error');
});
}]);
The following snippet creates a mock of the above service:
module(function($provide) {
$provide.service('demoService', function() {
this.isDemoApi = jasmine.createSpy('isDemoApi');
});
});
//Getting reference of the mocked service
var mockDemoSvc;
inject(function(demoService) {
mockDemoSvc = demoService;
});
To test the controller, firstly you will have to mock all the services which this controller is using by using the above code.
This would help to test controller APIs seperatly. Then you can go ahead to test APIs which are binded with the scope, like: toCountry()
I using this to save data to the service and called it again in the same controller after save it. But it didn't have value for the service. When i call the service again in another controller, it gave me the result i want.
.controller('HomeCtrl', function ($scope) {
$http.get(URL).success(function(data){
Service.thisData(data);
Service.getData(); // value = 1000
});
// call save data from service
// i didn't get any data from the service
Service.getData(); // value = undefined
};
So how do i get the data from service except inside the http.get??
Thanks.
I suggest you to write the api calls in a factory, and call that service in the controller, in which you need the data. You can call the same service in multiple controllers also.
As per my understanding, i hope this is your requirement. If not, please let me know, i will try my best.
You can refer to the plunkr code
http://plnkr.co/edit/G9MkVMSQ4VjMw8g2svkT?p=preview
angular.module('mainModule', [])
.factory('apiCallService', ['$http', '$q', '$log',
function ($http, $q, $log) {
var instance = {};
var config = null;
instance.apiCallToServer = function (config) {
var deferred = $q.defer();
$http(config)
.success(function (data, status, header, config) {
deferred.resolve(data);
})
.error(function (data, status, header, config) {
deferred.reject(status);
});
return deferred.promise;
};
return instance;
}])
.controller('FirstCtrl', ["$scope", "$log", "$location", "apiCallService",
function ($scope, $log, $location, apiCallService) {
var config = {
method: "get",
url: "/path"
};
$scope.successCallback = function (data) {
$log.log("success.");
$scope.data = data;
//data will be stored in '$scope.data'.
};
$scope.failureCallback = function (status) {
$log.log("Error");
};
apiCallService
.apiCallToServer(config)
.then($scope.successCallback, $scope.failureCallback);
}]);
I don't know how to pass data from the controller to the service as method arguments... I need to set in the controller the headers variable which is later added to the service which adds this later on to the http call
controller
angular.module('userControllers', []).
controller('userController', ['$scope', '$q', 'Users', '$location',
function($scope, $q, Users, $location) {
$scope.viewUser = function(userId) {
$location.path('/users/'+userId);
};
$scope.createUser = function () {
$location.path('/users/create');
};
headers = {service:'json',resource:'users'};
$scope.users = Users.query(headers);
$scope.users.$promise.then(function(result){
$scope.users = result;
});
}]);
service
angular.module('userServices', ['ngResource']).
factory('Users', ['$resource', '$q', function($resource, $q){
var factory = {
query: function (headerParams) {
var data = $resource('http://localhost:8080/gateway', {}, {
query: {
method: 'GET',
isArray: true,
headers: headerParams
}
});
var deferred = $q.defer();
deferred.resolve(data);
return deferred.promise;
}
}
return factory;
}]);
In this setup I'm getting Error: [$injector:unpr] Unknown provider: UserProvider <- User ... not really sure how to fix this one out ...
Later edit : code cleanup... posted full code and new error
TypeError: Cannot read property 'then' of undefined
at new <anonymous> (http://localhost/frontend-pdi-angular-js/js/controllers/users.js:16:38)
at invoke (https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular.js:3918:17)
at Object.instantiate (https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular.js:3929:23)
at https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular.js:7216:28
at link (https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular-route.js:913:26)
at nodeLinkFn (https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular.js:6648:13)
at compositeLinkFn (https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular.js:6039:13)
at publicLinkFn (https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular.js:5934:30)
at boundTranscludeFn (https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular.js:6059:21)
at controllersBoundTransclude (https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular.js:6669:18) <div ng-view="" class="ng-scope">
Later edit #2 : some working code, but using no ngResource whatsoever
angular.module('userControllers', ['userServices']).
controller('userController',
['$scope', '$q', 'Users', '$location', '$http',
function($scope, $q, Users, $location, $http) {
headerParams = {service:'json',resource:'users'};
$scope.users = $http({method: 'GET', url: 'http://localhost:8080/gateway', headers: headerParams, isArray:true}).
success(function(data, status, headers, config) {
$scope.users = data;
console.log(data);
}).
error(function(data, status, headers, config) {
console.log(status);
});
}]);
As others mentioned, you are using a non-existent reference to a provider ($promise) in your code. By simply setting the deferred to $scope.users and then acting on the 'then' of that deferred, you can solve this problem.
You can also simplify it a bit. You are setting the deferred on the $scope object and then you're overwriting that with the resolved value of the deferred. So you can do this instead:
Users.query(headers).then(function(result){
$scope.users = result;
});
if you want to assign the deferred to a separate variable:
var deferred = Users.query(headers);
deferred.then(function(result){
$scope.users = result;
});
angular.module('userControllers', ['userServices']).
controller('userController',
['$scope', '$q', 'Users', '$location',
function($scope, $q, Users, $location) {
headers = {service:'json',resource:'users'};
Users.query(headers).then(function(result){
$scope.users = result;
});
}])
angular.module('userServices', ['ngResource']).
factory('Users', ['$resource', '$q', function($resource, $q){
var factory = {
query: function (headerParams) {
var data = $resource('http://localhost:8080/gateway', {}, {
query: {
method: 'GET',
isArray: true,
headers: headerParams
}
});
var deferred = $q.defer();
deferred.resolve(data);
return deferred.promise;
}
}
return factory;
}]);
Try this. Your not include the userServices module into the userControllers module and you can omit $scope.users = Users.query(headers); part because if you put this this will call the query method.
replace controller with this code:
angular.module('userControllers', []). controller('userController', ['$scope', '$q', 'Users', '$location', function($scope, $q, Users, $location) {
$scope.viewUser = function(userId) {
$location.path('/users/'+userId);
};
$scope.createUser = function () {
$location.path('/users/create');
};
headers = {service:'json',resource:'users'};
Users.query(headers).$promise.then(function(result){
$scope.users = result;
});
}]);
and factory with this code :
angular.module('userServices', ['ngResource']).
factory('Users', ['$resource', '$q', function($resource, $q){
var factory = {
query: function (headerParams) {
return $resource('http://localhost:8080/gateway', {}, {
query: {
method: 'GET',
isArray: true,
headers: headerParams
}
})
}
}
return {
query: factory.query
}
}]);
i think this will help you.
I have a controller:
app.controller('ProductDetailCtrl', ['$scope', '$q', '$resource', 'Product', function($scope, $q, $resource, Product) {
$scope.product = {};
$scope.init = function(id)
{
$scope.product = Product.get({productId: id});
}
$q.all([$scope.product.$promise]).then(function() {
$scope.selected_color = $scope.product.products_colors[0];
});
// $scope.selected_color = $scope.product.products_colors[0];
}]);
And a factory:
app.factory('Product', ['$resource', function($resource) {
return $resource("/api/products/:productId", {}, {
query: {method: 'GET', isArray: true},
});
}]);
However, when I load the page it gives me this:
Error: $scope.product.products_colors is undefined #http://localhost:3000/assets/products/controllers/productscontroller.js?body=1:11:5 qFactory/defer/deferred.promise.then/wrappedCallback#http://localhost:3000/assets/angular.js?body=1:11499:15 qFactory/ref/<.then/<#http://localhost:3000/assets/angular.js?body=1:11585:11 $RootScopeProvider/this.$get</Scope.prototype.$eval#http://localhost:3000/assets/angular.js?body=1:12609:9 $RootScopeProvider/this.$get</Scope.prototype.$digest#http://localhost:3000/assets/angular.js?body=1:12421:15 $RootScopeProvider/this.$get</Scope.prototype.$apply#http://localhost:3000/assets/angular.js?body=1:12713:13 bootstrap/doBootstrap/<#http://localhost:3000/assets/angular.js?body=1:1420:9 invoke#http://localhost:3000/assets/angular.js?body=1:3919:7 bootstrap/doBootstrap#http://localhost:3000/assets/angular.js?body=1:1419:1 bootstrap#http://localhost:3000/assets/angular.js?body=1:1432:5 angularInit#http://localhost:3000/assets/angular.js?body=1:1345:5 #http://localhost:3000/assets/angular.js?body=1:21818:5 jQuery.Callbacks/fire#http://localhost:3000/assets/jquery.js?body=1:3100:1 jQuery.Callbacks/self.fireWith#http://localhost:3000/assets/jquery.js?body=1:3212:7 .ready#http://localhost:3000/assets/jquery.js?body=1:3424:3 completed#http://localhost:3000/assets/jquery.js?body=1:3454:3
return logFn.apply(console, args);
How would I wait until the $resource finishes loading? I want to set one of my $scope variables based on the result of Product.get(..). I know that Product.get(..) is returning and object with a products_colors property because console.log says so
Use a callback:
Product.get({productId: id}, function(data) {
$scope.product = data;
});