I've seen several posts about this, but I cannot adapt them to what I want to do.
I have a GET petition to a server to get a JSON response, and works like a charm.
.controller('ListController', ['$scope', '$http', function($scope, $http){
$http.get('js/data.json').success(function(data){
$scope.elements = data;
});
}])
Now I can print in the view with ng-repeat, all the elements within $scope.elements.
But now I need to do two different GETs, depending if has Internet, check online, if has not, check local DB.
I've created a factory like follows:
.factory('AttemptFactory', function($http){
var self = this;
this.elements = [];
this.returnFunction = function() {
$http.get('js/data.json').success(function(data){
self.elements = data;
});
}
}
I've firstly tried to fill this.elements, and then in the first controller access to it with $scope.newList = = AttemptFactory.elements.
Also tried make a return self.elements, and receive it with a AttemptFactory = AttemptFactory.returnFunction ();
I added the dependency injection of AttemptFactory to the .controller().
How can I archieve it??
if (online)
data = Get(from_server);
else
data = Get(from_local_DB);
return data;
Something like this. Thanks a lot.
I think you can try separating your concerns a bit. I'd recommend creating two factories: one that interacts with your server and one that interacts with the local db. Each could have a method that fetches data from the respective data store and returns a Promise for that data.
Then in your controller, you can choose which function to call based on the connection status. The function calls will both return a Promise that you can .then off of to get the data for your view. For example:
.factory('ServerFactory', function($http){
var serverFactory = {};
serverFactory.fetchJson = function() {
// note that we're returning the promise that $http get returns that will resolve with our data
return $http.get('js/data.json').success(function(res){
return res.data;
});
}
return serverFactory;
})
.factory('LocalDbFactory', function() {
var localDbFactory = {};
localDbFactory.fetchJson = function() {
// Return promise for JSON data here
}
return localDbFactory;
})
.controller('ListController', ['$scope', '$http', 'ServerFactory', 'LocalDbFactory', function($scope, ServerFactory, LocalDbFactory){
var result;
if (/* test if online */) {
result = ServerFactory.fetchJson();
} else {
result = LocalDbFactory.fetchJson();
}
result.then(function(data) {
$scope.elements = data;
})
}])
Related
(function () {
angular.module("app").controller('DashboardController', ['$q', 'dashboardService', function ($scope, $q,dashboardService) {
var DashboardController = this;
dashboardService.loadFromServer(DashboardController );
console.log("DashboardController ", DashboardController);
}])
})();
angular.module("app").service('dashboardService', ['$http', '$q', function ($http, $q) {
return {
loadFromServer: function (controller) {
var getDashboardEntries = $http.get('http://someUrl');
var getEmailData = $http.get('http://someOtherUrl');
var getSidebarData = $http.get('http://yetAnotherUrl');
return $q.all([getDashboardEntries, getSidebarData, getEmailData])
.then(function (results) {
controller.dashboardData = results[0].data;
controller.chartData = results[1].data;
controller.emailData = results[2].data;
});
},
};
}]);
1.The service returns the three bits of data and this is the results when logged using:
console.log("DashboardController ", DashboardController);
When I try to drill down on the data in this manner it logs "undefined"
console.log("DashboardController "DashboardController.dashboardData);
console.log("DashboardController "DashboardController.chartData);
console.log("DashboardController "DashboardController.emailData);
Do you realize that console.log is executed right after invoking loadFromServer before the server has chance to respond and promise resolves? The actual order is:
loadFromServer
console.log
promise success method - where you actually have your data
Change your controller's code to this:
dashboardService.loadFromServer(DashboardController ).then(function() {
console.log("DashboardController ", DashboardController);
});
What would be even better is to construct some object from parts of responses and assign it in the controller itself - not the service. In current implementation if you wanted to have another controller then service would assign response parts to same fields. I'd propose sth like this:
return $q.all([getDashboardEntries, getSidebarData, getEmailData])
.then(function (results) {
var data = {
dashboardData = results[0].data;
chartData = results[1].data;
emailData = results[2].data;
};
return data;
});
and then in controller:
dashboardService.loadFromServer().then(function(data) {
DashboardController.dashboardData = data.dashboardData;
DashboardController.chartData = data.chartData;
DashboardController.emailData = data.emailData;
});
In this solution the controller decides what to do with data, not the other way around.
<script>
var app = angular.module('myApp', ['ngMaterial']);
app.factory('factoryProvider', function ($http, $q) {
var facObj = {};
facObj.getLastWorkplace = $http.get('plugins/wcf-service/ServiceProvider.svc/getLastWorkPlacesJSON')
.then(function (response) {
return response.data;
});
return facObj;
});
app.controller('dashboardController', function ($scope, factoryProvider) {
factoryProvider.getLastWorkplace.then(function (successResponse) {
$scope.wp = successResponse;
console.log('inside');
console.log($scope.wp); // Return an object that I want
});
console.log('outside');
console.log($scope.wp); // $scope.wp is empty
});
The outside console log runs first, inside console log is the second. The problem is that $scope.wp can just get data in getLastWorkplace callback functions and it can not bind data to ng-model(using wp.property). How to solve it?
Thanks for your reading
You are assigning $scope.wp twice and the final assignment is the return value of your getLastWorkplace call (which you aren't returning anything.)
Change it to this...
factoryProvider.getLastWorkplace.then(function (successResponse) {
$scope.wp = successResponse;
});
or...
$scope.wp = factoryProvider.getLastWorkplace;
but not both.
I have been struggling to figure out the 'angular' way of working for using factories to represent ajax data (I do a lot of sharing between controllers using config). I have done some extensive research and all of the answers seem to fall in to two categories:
Use the factory to represent the data, then in the controller fetch the data and update the factory:
{not intended to be actual runable angular}
var app = angular.module('main', [])
app.factory('data', function($http){
var data = []
return data
})
app.controller('cntrl', [$scope, $http, data]){
$scope.data = data
$http.get('/data').success(
function(idata){
$scope.data = idata
)
}
Use the factory to represent a http request with a promise and then assign that data to the $scope
var app = angular.module('main', [])
app.factory('data', function ($http, $q){
var factory = {};
factory.getdata = function(){
var defer = $q.defer();
$http.get('/data').success(function(idata) {
defer.resolve(idata);
})
return defer.promise;
}
return factory;
});
app.controller('cntrl', [$scope, data]){
$scope.data = []
data.getdata().then(function(idata) { $scope.data = idata });
}
I have several issues with this, in the first category the factory looks mega lame and like its not adding much value and each controller needs to have the code to manipulate the factory. With the second controller, the factory is very complicated and doesn't actually store the data (for sharing between controllers), just represents a long winded way to write a http.get
My question is this:
How can I use a factory to represent my data and manage the manipulation of that data in a clean way? What I would like to do is something like this:
var app = angular.module('main',[])
app.factory('data', function($http){
var factory = {}
factory.data = []
factory.initialise = function(){
$http.get('/data').success(
function(data){
factory.data = data
}
)
}
return factory
})
app.controller('cntrlA', [$scope, data]) {
$scope.data = data.data
$data.initialise()
}
app.controller('cntrlB', [$scope, data]) {
$scope.data = data.data
}
I am working on a project with a similar issue. What I ended up doing is using the service as a repository of the data. Instead of the initialize function getting fresh data each time, it is getting fresh data only if the data repository is empty or a getFresh flag is tripped (manual override). This way each controller can call the same function to get the data and manipulate it as the controller needs without tampering with the original data.
var app = angular.module('main',[])
app.factory('dataService', function($http, $q){
var factory = {}
factory.data = []
factory.getFresh = false
factory.initialise = function(){
var deferred = $q.defer()
if(factory.data.length===0 || factory.getFresh) {
$http.get('/data').success(function(data){
factory.data = data
deferred.resolve(data)
})
}
else{
deferred.resolve(factory.data)
}
return deferred.promise
}
return factory
})
app.controller('cntrlA', [$scope, dataService]) {
$scope.data = []
dataService.initialise().then(function(data){
$scope.data = data
}
}
app.controller('cntrlB', [$scope, dataService]) {
$scope.data = []
dataService.initialise().then(function(data){
$scope.data = data
}
}
What I am trying to achieve:
Pass the parameter specId from the controller to the factory
In the factory: perform a $http.get to get JSON data
Return the JSON data to the controller
Displaying the information by assigning it to the $scope.formData
I tried many different ways but it only returns undefined. Before I created the factory I performed the $http.get directly in the controller without any issues, but now I am trying to structure the app in a better way.
Factory:
app.factory("dataFactory", function($http) {
var factory = {};
factory.getSpec = function(specId) {
return $http.get('getSpec.aspx?specId=' + specId)
};
return factory;
});
Controller
app.controller('EditSpecController', function ($scope, $stateParams, $http, dataFactory) {
$scope.specId = $stateParams.specId;
$scope.formData = [];
if($scope.specId) { //If EDIT MODE
dataFactory.getSpec($scope.specId).then(function(response) {
$scope.formData = response.data;
$scope.use_unit = response.data.use_unit;
});
}
As you noticed $http returns promise already, so you should do something more like this
factory.getSpec = function(specId) {
return $http.get('getSpec.aspx' + specId)
};
and then in controller
dataFactory.getSpec().then(function(response) {
$scope.formData = response.data;
});
I'm using a service to make user data available to various controllers in my Angular app. I'm stuck trying to figure out how to use the $http service to update a variable local to the service (in my case "this.users"). I've tried with and without promises. The server is responding correctly.
I've read several excellent articles for how to use $http within a service to update the scope of a controller. The best being this one: http://sravi-kiran.blogspot.com/2013/03/MovingAjaxCallsToACustomServiceInAngularJS.html. That does not help me though because it negates the benefits of using a service. Mainly, modifying the scope in one controller does not modify throughout the rest of the app.
Here is what I have thus far.
app.service('UserService', ['$http', function($http) {
this.users = [];
this.load = function() {
var promise = $http.get('users.json')
.success(function(data){
// this.users is undefined here
console.log(this.users);
}
};
promise.then(function() {
// this.users is undefined here
console.log('this.users');
});
}]);
Any help is greatly appreciated. Thank you.
Try using
var users = [];
rather than
this.users = [];
and see what
console.log(users);
outputs in each of those cases.
Your service is oddly defined, but if you have a return in it you can access it from any controller:
app.service('UserService', ['$http', function($http) {
var users = [];
this.load = function() {
var promise = $http.get('users.json')
.success(function(data){
// this.users is undefined here
console.log(users);
users = data.data;
}
};
return {
getUsers: function(){
return users;
}
}
}]);
so in your controller, you can use:
var myUsers = UserService.getUsers();
UPDATE to use a service correctly here, your service should return a promise and the promise should be accessed in the controller: Here's an example from another answer I gave
// your service should return a promise
app.service('PickerService', [$http', function($http) {
return {
getFiles: function(){
return $http.get('files.json'); // this returns a promise, the promise is not executed here
}
}
}]);
then in your controller do this:
PickerService.getFiles().then(function(returnValues){ // the promise is executed here as the return values are here
$scope.myDirectiveData = returnValues.data;
});
this does not have scope anymore where you are trying to use it do this instead:
app.service('UserService', [$http', function($http) {
var users = [];
this.load = function() {
var promise = $http.get('users.json')
.success(function(data){
console.log(users);
}
};
promise.then(function() {
console.log(users);
});
}]);
all local variables to a service should just be vars if you assign them to this as a property than they will be included every time the service is injected into a controller which is bad practice.
I think what your asking for is a solution along the lines of defining your service like this:
angular.module('app')
.service('User', function($http, $q) {
var users = null;
var deferred = $q.defer()
return {
getUsers: function() {
if(users) {
deferred.resolve(users);
} else {
$http.get('users.json');
.success(function(result) {
deferred.resolve(result);
})
.error(function(error) {
deferred.reject(error);
});
}
return deferred.promise;
}
};
});
Then in one Each controller you would have to do this:
angular.module('app')
.controller('ACtrl', function($scope, User) {
User.getUsers().then(function(users) {
// Same object that's in BCtrl
$scope.users = users;
});
});
angular.module('app')
.controller('BCtrl', function($scope, User) {
User.getUsers().then(function(users) {
// Same object that's in ACtrl
$scope.users = users;
});
});
NOTE: Because the deferred.promise the same promise passed to all controllers, executing deferred.resolve(users) in the future will cause all then success callbacks in each of your controllers to be called essentially overwriting the old users list.
All operations on the list will be noticed in all controllers because the users array is a shared object at that point. This will only handle updates to the user list/each individual user on the client side of your application. If you want to persist changes to the server, you're going to have to add other $http methods to your service to handle CRUD operations on a user. This can generally be tricky and I highly advise that you check out ngResource, which takes care of basic RESTful operations