I was recomended to use Angular services in order to centralize many repetative functions that were store in my controller, so I am rewriting my code using services now.
It seemed simple at first but cant seem to find a good structure to fetch my ajax data (only once), then store it in my service for my controller to reuse the cached data when ever it needs it. At the moment I keep getting errors saying: TypeError: Cannot read property 'sayHello' of undefined.
I believe this is because theres is a delay to fetch my ajax data via my service before the controller loads. Im not quite certain how I can optimize this. Would anyone have a better stucture to this?
Service:
app.service('MyService', function ($http) {
this.sayHello = function () {
var deferred = $q.defer();
$http({
method: 'GET',
url: 'AJAX PATH',
headers: { "Accept": "application/json;odata=verbose;charset=utf-8"}
}).then(function(data){
var configurations = data;
var configurations.data_result_1 = configurations.data_result_1.split("\r\n");
var configurations.data_result_2 = configurations.data_result_2.split("\r\n");
deferred.resolve(configurations);
}
return deferred.promise;
};
this.sayHello(); //Run the function upon page laod.
});
Controller:
app.controller('AppController', function (MyService, $scope) {
$scope.configurations = null;
$scope.configurations = function() { MyService.sayHello() };
});
I recommend you to use another way to declare the service:
app.factory("MyService", function($http){
var configurations = {};
$http({
method: 'GET',
url: 'AJAX PATH',
headers: { "Accept": "application/json;odata=verbose;charset=utf-8"}
}).then(function(data){
configurations = data;
configurations.data_result_1 = configurations.data_result_1.split("\r\n");
configurations.data_result_2 = configurations.data_result_2.split("\r\n");
});
return {
getConfigurations: function(){
return configurations;
}
}
In your controller you can use a $watch, then when the configurations objects changes you take the information:
.controller("YourCtrl", function($scope,MyService){
var vm = this;
vm.configurations = {};
$scope.$watchCollection(function () { return MyService.getConfigurations()},function(newValue){
vm.configurations = newValue;
});
Totally agree with Bri4n about store configuration in the factory. Not agree about the controller because you said you don't want to watch, but only load data once.
But you $http already return a promise so as Brian said this is nice (just $q is useless here so you can delete it from injection). And I just wrapped http call in function, and the exposed function just check if configurations are already loaded. If yes, just return configurations else load it and then return it.
app.factory("MyService", function($http,$q){
var configurations = {};
function loadConfig(){
$http({
method: 'GET',
url: 'AJAX PATH',
headers: { "Accept": "application/json;odata=verbose;charset=utf-8"}
}).then(function(data){
configurations = data;
configurations.data_result_1 = configurations.data_result_1.split("\r\n");
configurations.data_result_2 = configurations.data_result_2.split("\r\n");
});
}
return {
getConfigurations: function(){
If( !!configurations ){
return configurations;
}
//Else loadConfig.then return configurations
}
}
In your controller you can just get config without need to know if it is already loaded.
.controller("YourCtrl", function(MyService){
var vm = this;
// If configurations already loaded return config, else load configurations and return configurations.
vm.configurations = MyService.getConfigurations();
I write on my phone so my code is not perfect I can't write properly.
OK, on second thought, it looks like you are not using the dependency array notation properly. Change your code to:
app.service('MyService', ['$http', function ($http) {
// do your stuff here
}]);
and for the controller:
app.controller('AppController', ['MyService', '$scope', function(MyService, $scope) {
// do your stuff here
}]);
I am trying to implement ngTable to display json from rest call. In my factory js, I defined the method for the http get request to obtain all records in this case.
ristoreApp.factory("fmFactory", ['$http', '$window',
function ($http, $window) {
var service = {};
service.getAll = function () {
var url = SERVER + "/ristore/foundation/";
return $http({
headers: {'Authorization': 'Bearer ' + $window.localStorage.getItem("access_token")},
url: url,
method: 'GET',
crossOrigin: true
})
}
return service;
}]);
ngTable is set up in my controller js
ristoreApp.controller("fmCtrl",
['$scope', 'fmFactory', 'NgTableParams', function($scope, fmFactory, NgTableParams) {
$scope.selection = '0';
$scope.reports = [];
$scope.fmSearch = function () {
if ($scope.selection == '0') {
fmFactory.getAll().success(function (data) {
$scope.reports = data;
$scope.tableParams = new NgTableParams({
page: 1, // show first page
count: 10 // count per page
}, {
total: $scope.reports.length, // length of data
getData: function ($defer, params) {
$defer.resolve($scope.reports.slice((params.page() - 1) * params.count(), params.page() * params.count()));
}
});
})
}
}
}]
)
Nothing fancy, just simple pagination with 10 records per page. However, I got error TypeError: Cannot read property 'page' of undefined for the method params.page() in $defer.resolve(). This is very strange. Apparently 'page' is defined in the parameter section of NgTableParams. Why does it complain it is not defined?
EDIT:
Based on the link from Sergii's answer I removed the $defer and changed my controller js to the following:
ristoreApp.controller("fmCtrl",
['$scope', 'fmFactory', 'NgTableParams', function($scope, fmFactory, NgTableParams) {
$scope.selection = '0';
$scope.reports = [];
$scope.fmSearch = function () {
if ($scope.selection == '0') {
$scope.tableParams = new NgTableParams({
page: 1, // show first page
count: 10 // count per page
}, {
getData: function (params) {
return fmFactory.getAll().then(function(data) {
params.total(data.inlineCount);
return data;
});
}
});
}
}
}]
)
And yet nothing displayed but a bunch of lines. The http call url has been tested and returns correct promise using rest api tester.
As I wrote in comment params is undefined, but exception was wrapped\processed by angular that is reason of partly correct exception information.
I believe this problem appears because of you are using newest ng-table-1.0.0 library for now. If you navigate to Angular ngTableDynamic example:server-side list or Angular ngTable example:server-side list please pay attantion that API to load data was changed.
getData: function(params) {
// ajax request to api
return yourFactory.get(params.url()).$promise.then(function(data) {
params.total(data.inlineCount);
return data.results;
});
}
In your parameter $defer also different object (object is params). If you'll try apply provided solution, please make sure that you changed correctly parameters:
params.url() - should be pagination filter like {page: 0, size: 10}
data.inlineCount - total elements on server side
data.results - data list from server side
I hope my investigation helped not only me to fix this problem.
I am new to Angularjs and studied a lot. But I stuck at a point. Google doesn't help me. I have a controller and I have data in $scope.results
app.controller('manage_categories', function($scope, $http, $filter, $window) {
$scope.results = [];
$http({
url: base_url + 'employee/fetchData?table=results',
method: "POST",
}).success(function(data) {
$scope.results = data;
});
})
now i want to access the same in other without any other $http call. I have done with another call but i don't want this . because i need this in many other controllers.something like this
app.controller('manage_users', function($scope, $http, $filter, $window,results) {
$scope.results = results;
//~ $http({
//~ url: base_url + 'employee/fetchData?table=results',
//~ method: "POST",
//~ }).success(function(data) {
//~ $scope.results = data;
//~ });
})
or any other method. Thanks.
update
I tried this
var myApp = angular.module('myApp',[]);
myApp.factory('results', function() {
return {
name : [{id:21,name:'this is test'}]
};
});
app.controller('manage_users', function($scope, $http, $filter, $window,results) {
$scope.results = results;
})
This is working fine . But not working with $http call .
var myApp = angular.module('myApp',[]);
myApp.factory('results', function($scope,$http) {
$scope.results=[];
$http({
url: base_url + 'employee/fetchData?table=results',
method: "POST",
}).success(function(data) {
$scope.results = data;
});
return {
name : results
};
});
update 2
after answers i write it like
var canapp = angular.module('canApp', ["ngRoute", "angularFileUpload"]);
canapp.service('ResultsFactory', ['$http', function($http) {
// http call here
var url=base_url + 'employee/fetchData?table=results';
$http.post(url,data).success(function(data){
this.results = data;
});
}])
call like this
canapp.controller('get_candidates', function($scope, $http, $filter, $timeout, $window, ResultsFactory) {
$scope.check=ResultsFactory.results;
});
but it is not setting the value in template
Use $broadcast to share the data between controllers. Your code will look like this
app.controller('manage_categories', function($scope, $http, $filter, $window, $rootScope) {
$scope.results = [];
$http({
url: base_url + 'employee/fetchData?table=results',
method: "POST",
}).success(function(data) {
$scope.results = data;
$rootScope.$broadcast("results",data);
});
});
app.controller('otherCtrlr', function($scope, $rootScope) {
$rootScope.$on("results", function(event, data){
$scope.results = data;
});
});
But using a service call in the controller is not a best approach. Create a factory and create a method to call your service.
From controller you need to call this method. But to avoid two service calls, you definitely need to use broadcast/emit(depending on data transfer is from parent or child)
There are various possible way of communicating between two controllers. If you just Google share data between controllers angularjs, you may found various links:
Using Services to Share Data Between Controllers
Sharing Data Between Controllers
Share data between AngularJS controllers
Passing data between controllers in Angular JS?
So, in short, possible ways are:
Using Angular Factories (recommended)
Using $rootScope (not recommended)
Using top most controller's scope as root scope
You can do this:
app.factory('ResultsFactory', resultsFactory);
resultsFactory.$inject = ['$http'];
function resultsFactory = function(){
var self = {};
var results = null;
self.getResults = function(){
if(!results){
$http.post(url,data).success(function(data){
results = data;
});
}else{
return results;
}
}
return self;
}
Only the first time that you call to ResultsFactory.getResults() this executes the $http call.
Here's a small fiddle explaining how to share data between controllers.
https://jsfiddle.net/frishi/zxnLwz6d/10/
(Check the browser console to see that both controllers can access data via the service.)
Basically the premise of a service is that it is a singleton that can be used by all the controllers registered on your module.
You want to make that $http call in a service:
.service('myService', ['$http', function($http) {
this.getData = function(){
// Simple GET request example:
return $http({
method: 'GET',
url: 'https://api.github.com/users/mralexgray/repos' // example API
}).then(function successCallback(response) {
return response;
}, function errorCallback(response) {
// return error message
});
}
}])
In your controller:
.controller('Controller2',['$scope','myService',function ($scope,myService) {
$scope.foo = myService.getData();
//resolve the promise:
$scope.foo.then(function(data){
console.log(data);
})
}
])
It is strongly recommended to use separated services as frishi pointed out. This sample is in single file and module just to make it readeable. Following implementation stores the promise and actual request is only made on the initial call to getFoo. The rest will get the response from the in memory promise.
'use strict';
angular.module('foo', [])
.factory('FooResource', function SessionResource($http) {
var fooPromise;
return {
getFoo: function getFoo() {
if(!fooPromise) {
fooPromise = $http.post('employee/fetchData?table=results');
}
return fooPromise;
}
};
})
.controller('FooController', function($scope, FooResource) {
FooResource.getFoo().then(function getFooSuccess(data) {
$scope.results = data;
});
});
I use this angular code with ionic framework
may be its help you..
my factory is..
angular.module('starter.services', [])
.factory('Chats', function() {
// Might use a resource here that returns a JSON array
// Some fake testing data
var chats = [{
id: 0,
name: 'Ben Sparrow',
lastText: 'You on your way?',
face: 'img/ben.png'
}, {
id: 1,
name: 'Max Lynx',
lastText: 'Hey, it\'s me',
face: 'img/max.png'
}, {
id: 2,
name: 'Adam Bradleyson',
lastText: 'I should buy a boat',
face: 'img/adam.jpg'
}, {
id: 3,
name: 'Perry Governor',
lastText: 'Look at my mukluks!',
face: 'img/perry.png'
}, {
id: 4,
name: 'Mike Harrington',
lastText: 'This is wicked good ice cream.',
face: 'img/mike.png'
}];
return {
all: function() {
return chats;
},
remove: function(chat) {
chats.splice(chats.indexOf(chat), 1);
},
get: function(chatId) {
for (var i = 0; i < chats.length; i++) {
if (chats[i].id === parseInt(chatId)) {
return chats[i];
}
}
return null;
}
};
});
and i use this factory in many controllers
.controller('ChatsCtrl', function($scope, Chats) {
$scope.chats = Chats.all();
$scope.remove = function(chat) {
Chats.remove(chat);
};
})
.controller('ChatDetailCtrl', function($scope, $stateParams, Chats) {
$scope.chat = Chats.get($stateParams.chatId);
})
in this code factory hit http:// request only one time and i use response on two controllers.
Hope its help you.
I'm trying to make an update function to replace some data on an user.
I've created the factory:
factory('Details', ['$resource', function ($resource) {
return $resource('/api/client/:id', null, {
'update': { method: 'PUT'}
});
}]);
and the controller:
.controller('ClientDetails', function ($scope, Details, $routeParams) {
$scope.client = Details.query({ id: $routeParams.id });
$scope.editClient = function () {
$scope.client.$update();
}
});
and when entering function editClient() it throws and error:
$scope.client.$update is not a function
What have I done wrong? Thanks
By default, the query method is defined to return an array of instances: 'query': {method:'GET', isArray:true}, see documentation for ng-resource. And the array does not have the $update method. From your code, you need to use the get to fetch the instance, like this:
$scope.client = Details.get({ id: $routeParams.id });
$scope.editClient = function () {
$scope.client.$update();
}
I'm following a Tuts+ tutorial on building an AngularJS webapp. Everything went well untill I tried getting JSON data on the screen.
I keep getting the following Error: [$resource:badcfg]
Here's my code:
Service
angular.module('ContactsApp')
.factory('Contact', function ($resource) {
return $resource('/api/contact/:id', { id: '#id' }, {
'update': { method: 'PUT' }
});
})
Controller
angular.module('ContactsApp')
.controller('ListController', function($scope, Contact) {
$scope.contacts = Contact.query();
$scope.fields = [ 'firstName', 'lastName'];
$scope.sort = function(field){
$scope.sort.field = field;
$scope.sort.order = !$scope.sort.order;
};
$scope.sort.field = 'firstName';
$scope.sort.order = false;
});
I have allready searched the web for solutions and tried adding isArray:false to the declaration. I also compared all my code to the full code on GitHub, but I can't find the problem.
Did you try to pass an empty array when defining your module, like this :
angular.module('ContactsApp', [])