I've been struggling with this for a few days now and can't seem to find a solution.
I have a simple listing in my view, fetched from MongoDB and I want it to refresh whenever I call the delete or update function.
Although it seems simple that I should be able to call a previously declared function within the same scope, it just doesn't work.
I tried setting the getDispositivos on a third service, but then the Injection gets all messed up. Declaring the function simply as var function () {...} but it doesn't work as well.
Any help is appreciated.
Here's my code:
var myApp = angular.module('appDispositivos', []);
/* My service */
myApp.service('dispositivosService',
['$http',
function($http) {
//...
this.getDispositivos = function(response) {
$http.get('http://localhost:3000/dispositivos').then(response);
}
//...
}
]
);
myApp.controller('dispositivoController',
['$scope', 'dispositivosService',
function($scope, dispositivosService) {
//This fetches data from Mongo...
$scope.getDispositivos = function () {
dispositivosService.getDispositivos(function(response) {
$scope.dispositivos = response.data;
});
};
//... and on page load it fills in the list
$scope.getDispositivos();
$scope.addDispositivo = function() {
dispositivosService.addDispositivo($scope.dispositivo);
$scope.getDispositivos(); //it should reload the view here...
$scope.dispositivo = '';
};
$scope.removeDispositivo = function (id) {
dispositivosService.removerDispositivo(id);
$scope.getDispositivos(); //... here
};
$scope.editDispositivo = function (id) {
dispositivosService.editDispositivo(id);
$scope.getDispositivos(); //... and here.
};
}
]
);
On service
this.getDispositivos = function(response) {
return $http.get('http://localhost:3000/dispositivos');
}
on controller
$scope.addDispositivo = function() {
dispositivosService.addDispositivo($scope.dispositivo).then(function(){
$scope.getDispositivos(); //it should reload the view here...
$scope.dispositivo = '';
});
};
None of the solutions worked. Later on I found that the GET request does execute, asynchronously however. This means that it loads the data into $scope before the POST request has finished, thus not including the just-included new data.
The solution is to synchronize the tasks (somewhat like in multithread programming), using the $q module, and to work with deferred objects and promises. So, on my service
.factory('dispositivosService',
['$http', '$q',
function($http, $q) {
return {
getDispositivos: function (id) {
getDef = $q.defer();
$http.get('http://myUrlAddress'+id)
.success(function(response){
getDef.resolve(response);
})
.error(function () {
getDef.reject('Failed GET request');
});
return getDef.promise;
}
}
}
}
])
On my controller:
$scope.addDispositivo = function() {
dispositivosService.addDispositivo($scope.dispositivo)
.then(function(){
dispositivosService.getDispositivos()
.then(function(dispositivos){
$scope.dispositivos = dispositivos;
$scope.dispositivo = '';
})
});
};
Being my 'response' object a $q.defer type object, then I can tell Angular that the response is asynchronous, and .then(---).then(---); logic completes the tasks, as the asynchronous requests finish.
Related
UPDATE
I am currently doing this, and I'm not sure why it works, but I don't think this is the correct approach. I might be abusing digest cycles, whatever those are.
First, I want to have the array navigationList be inside a service so I can pass it anywhere. That service will also update that array.
app.factory('ChapterService', [ 'ExtService', function(ExtService) {
var navigationList = [];
var getNavigation = function() {
ExtService.getUrl('navigation.json').then(function(data) {
angular.copy(data.navigationList, navigationList);
});
}
return{
getNavigation: getNavigation,
navigationList: navigationList,
}
}]);
Then in my controller, I call the service to update the array, then I point the scope variable to it.
ChapterService.getNavigation();
$scope.navigationList = ChapterService.navigationList;
console.log($scope.navigationList);
But this is where it gets weird. console.log returns an empty array [], BUT I have an ng-repeat in my html that uses $scope.navigationList, and it's correctly displaying the items in that array... I think this has something to do with digest cycles, but I'm not sure. Could anyone explain it to me and tell me if I'm approaching this the wrong way?
I have a main factory that runs functions and calculations. I am trying to run
app.factory('ChapterService', [ 'ExtService', function(ExtService) {
var navigation = {
getNavigationData : function () {
ExtService.getUrl('navigation.json').then(function(data) {
return data;
});
}
}
return: {
navigation: navigation
}
I did a console.log on the data before it gets returned and it's the correct data, but for some reason, I can't return it..
The ExtService that has the getUrl method is just the one that's typically used (it returns a promise)
In my controller, I want to do something like
$scope.navigation = ChapterService.navigation.getNavigationData();
in order to load the data from the file when the app initializes,
but that doesn't work and when I run
console.log(ChapterService.navigation.getNavigationData());
I get null, but I don't know why. Should I use app.run() for something like this? I need this data to be loaded before anything else is done and I don't think I'm using the best approach...
EDIT
I'm not sure if I should do something similar to what's being done in this jsfiddle, the pattern is unfamiliar to me, so I'm not sure how to re purpose it for my needs
My code for ExtService is
app.factory('ExtService', function($http, $q, $compile) {
return {
getUrl: function(url) {
var newurl = url + "?nocache=" + (new Date()).getTime();
var deferred = $q.defer();
$http.get(newurl, {cache: false})
.success(function (data) {
deferred.resolve(data);
})
.error(function (error) {
deferred.reject(error);
});
return deferred.promise;
}
}
});
EDIT 2
I'd like to separate the request logic away from the controller, but at the same time, have it done when the app starts. I'd like the service function to just return the data, so I don't have to do further .then or .success on it...
You are using promises incorrectly. Think about what this means:
var navigation = {
getNavigationData : function () {
ExtService.getUrl('navigation.json').then(function(data) {
return data;
});
}
}
getNavigationData is a function that doesn't return anything. When you're in the "then" clause, you're in a different function so return data only returns from the inner function. In fact, .then(function(data) { return data; }) is a no-op.
The important thing to understand about promises is that once you're in the promise paradigm, it's difficult to get out of it - your best bet is to stay inside it.
So first, return a promise from your function:
app.factory('ChapterService', [ 'ExtService', function(ExtService) {
var navigation = {
getNavigationData: function () {
return ExtService.getUrl('navigation.json');
}
}
return {
navigation: navigation
}
}])
Then use that promise in your controller:
app.controller('MyController', function($scope, ExtService) {
ExtService
.navigation
.getNavigationData()
.then(function(data) {
$scope.navigation = data;
});
})
Update
If you really want to avoid the promise paradigm, try the following, although I recommend thoroughly understanding the implications of this approach before doing so. The object you return from the service isn't immediately populated but once the call returns, Angular will complete a digest cycle and any scope bindings will be refreshed.
app.factory('ChapterService', [ 'ExtService', function(ExtService) {
var navigation = {
getNavigationData: function () {
// create an object to return from the function
var returnData = { };
// set the properties of the object when the call has returned
ExtService.getUrl('navigation.json')
.then(function(x) { returnData.nav = x });
// return the object - NB at this point in the function,
// the .nav property has not been set
return returnData;
}
}
return {
navigation: navigation
}
}])
app.controller('MyController', function($scope, ExtService) {
// assign $scope.navigation to the object returned
// from the function - NB at this point the .nav property
// has not been set, your bindings will need to refer to
// $scope.navigation.nav
$scope.navigation = ExtService
.navigation
.getNavigationData();
})
You are using a promise, so return that promise and use the resolve (.then) in the controller:
app.factory('ChapterService', [ 'ExtService', function(ExtService) {
var navigation = {
getNavigationData: function () {
return ExtService.getUrl('navigation.json'); // returns a promise
});
}
return: {
navigation: navigation
}
}
controller:
ChapterService
.navigation
.getNavigationData()
.then(function (data) {
// success
$scope.navigation = data;
}, function (response) {
// fail
});
This is a different approach, I don't know what your data looks like so I am not able to test it for you.
Controller
.controller('homeCtrl', function($scope, $routeParams, ChapterService) {
ChapterService.getNavigationData();
})
Factory
.factory('ChapterService', [ 'ExtService', function(ExtService) {
function makeRequest(response) {
return ExtService.getUrl('navigation.json')
}
function parseResponse(response) {
retries = 0;
if (!response) {
return false;
}
return response.data;
}
var navigation = {
getNavigationData: function () {
return makeRequest()
.then(parseResponse)
.catch(function(err){
console.log(err);
});
}
}
return navigation;
}])
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/
I would like to make a service which loads a JSON file and the provide some methods to work with the result.
Blog.service('ArticleService', ['$http', function ($http) {
this.loadArticles = function() {
return $http.get('data/articles.json');
};
this.getArticles = function () {
// return the json
};
this.getArticle = function (id) {
// work with the json
};
}]);
And the controller:
Blog.controller('BlogController', function ($scope, ArticleService) {
console.log(ArticleService.getArticles());
console.log(ArticleService.getArticle(1));
});
I'd like to cache the result of my request and then work with this result in my methods, getArticles and getArticle.
Following your comment, if you want to do only 1 request, then I'll suggest saving the $http promise in a variable in your service and returning that variable in getArticles.
Blog.service('ArticleService', ['$http', function ($http) {
this.loadArticles = function() {
return $http.get('data/articles.json');
};
var articles;
this.getArticles = function () {
if (!articles) {
articles = this.loadArticles();
}
return articles;
};
this.getArticle = function (id) {
// work with the json
};
}]);
Or better yet, load the articles variable on init:
Blog.service('ArticleService', ['$http' '$q', function ($http, $q) {
var articles = (function() {
return $http.get('data/articles.json');
})();
this.getArticles = function () {
return articles;
};
this.getArticle = function (id) {
// Return a promise that will be resolved with the correct article.
var deferred = $q.defer();
articles.then(function (arts) {
arts.forEach(function (art) {
if (art.id === id) {
deferred.resolve(art);
}
});
});
return deferred.promise;
};
}]);
Angular used to unwrap promises, but that was deprecated because it proved to be troublesome and very "magical" (leading to misunderstandings). See https://github.com/angular/angular.js/issues/5153
Alternatively, you could use $http's cache property:
Blog.service('ArticleService', ['$http', function ($http) {
this.getArticles = function() {
return $http.get('data/articles.json', {cache: 'myCache'});
};
this.getArticle = function (id) {
return $http.get('data/articles.json', {cache: 'myCache'}).then(function(response) {
// Parse the response, return the article in question.
});
};
}]);
Blog.service('ArticleService', ['$http', '$q', function ($http, $q)
{
var self = this;
this.loadArticles = function ()
{
var deffered = $q.defer();
$http.get("data/articles.json").then(
function (result)
{
deffered.resolve(result);
},
function ()
{
deffered.reject()
}
);
return deffered.promise;
};
this.getArticles = function ()
{
return self.loadArticles;
};
this.getArticle = function (id)
{
// some stuff to retrieve the object
};
}]);
then in your controller
Blog.controller('BlogController', function ($scope, ArticleService)
{
ArticleService.getArticles().then(function (data)
{
console.log(data);
}, then(function ()
{
alert("error")
})
);
});
});
If you want your cache to persist from session to session, then see user3904's answer.
If it is acceptable for the cache to die with the page, then you might consider the following approach :
Blog.service('ArticleService', ['$http', '$q', function ($http, $q) {
var articles = {};
var loadArticles = function () {
articles.multi = $http.get("data/articles.json").done(function(a) {
articles.multi = a;
});
return articles.multi;
};
var loadArticle = function (id) {
articles[id] = $http.get("data/articles.json/" + id).done(function(a) {
articles[id] = a;
});
return articles[id];
};
this.getArticles = function () {
return articles.multi ? $q.when(articles.multi) : loadArticles();
};
this.getArticle = function(id) {
id = '' + id;
return articles[id] ? $q.when(articles[id]) : loadArticle(id);
};
}]);
Explanation
The cache is the js plain object articles.
The two load...() functions are private - assuming, as seems reasonable, that they are to be called only by their corresponding get...() functions. (This is not particularly significant to the overall solution).
On first call, the two loadArticles() caches a promise, which is overwritten with the data delivered by the promise, when that data arrives. The promise is temporarily cached for one reason - in case a second request is made before the data is returned from the server. The $q.when(...) in this.getArticles() ensures that loadArticles() returns promise-wrapped data, regardless of what is currently cached - promise or data.
this.getArticle() works similarly to this.getArticles(), but accepts (and passes) an id.
This strategy is designed to be storage efficient. By caching only the data long term, the (small) overhead of promise wrappers is avoided. This might be an issue if users are likely to request many individual articles per session.
Storage efficiency is bought at the cost of slightly less efficient delivery of cached articles. $q.when(...) will take some extra clock cycles to complete.
It should be quite simple to adapt the code to work the other way round - ie to optimise for delivery at the cost of storage efficiency - ie to store promises long term and avoid the need for $q.when(...).
Both approaches will work and for most applications it's pretty academic which one is adopted.
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
I have a service that fetches some client data from my server:
app.factory('clientDataService', function ($http) {
var clientDataObject = {};
var cdsService = {
fetch: function (cid) {
//$http returns a promise, which has a then function, which also returns a promise
var promise = $http.get('/clients/stats/' + cid + '/').then(function (response) {
// The then function here is an opportunity to modify the response
console.log(response);
// The return value gets picked up by the then in the controller.
clientDataObject = {'data': response.data, 'currentClientID': cid};
return clientDataObject;
});
// Return the promise to the controller
return promise;
}
};
return cdsService;
});
Then in one controller I do:
//get stats
clientDataService.fetch($scope.id).then(function (response) {
$scope.client_data = {
'statistics': response.data
}
});
Which all works very well. However, I'm trying to do a watch from another controller on that service to update it's scope when the data changes, rather then having to re-kick off the http request:
$scope.$watch('clientDataService.clientDataObject', function (cid) {
alert(cid);
});
I'm just alerting for now, but it never ever triggers. When the page initially loads, it alerts "undefined". I have no errors in the console and all the $injects are fine, but it never seems to recognize that data has changed in the service. Am I doing something wrong in the watch?
Many thanks
Ben
clientDataService.clientDataObject is not part of your controller's scope, so you can't watch for changes on that object.
You need to inject the $rootScope into your service then broadcast the changes to the controllers scopes.
app.factory('clientDataService', function ($rootScope, $http) {
var clientDataObject = {};
var cdsService = {
fetch: function (cid) {
var promise = $http.get('/clients/stats/' + cid + '/').then(function (response) {
// The then function here is an opportunity to modify the response
console.log(response);
// The return value gets picked up by the then in the controller.
clientDataObject = {'data': response.data, 'currentClientID': cid};
$rootScope.$broadcast('UPDATE_CLIENT_DATA', clientDataObject);
return clientDataObject;
});
// Return the promise to the controller
return promise;
}
};
return cdsService;
});
Then in the controller you can listen for the change using:
$scope.$on('UPDATE_CLIENT_DATA', function ( event, clientDataObject ) { });
Another approach can be:
define new service
app.factory('DataSharingObject', function(){
return {};
}
include this new service in controller where we want to store the data
app.factory('clientDataService', function ($http, DataSharingObject) {
DataSharingObject.sharedata = ..assign it here
}
include this new service in controller where we want to access the data
app.factory('clientReceivingService', function ($http, DataSharingObject) {
..use it here... = DataSharingObject.sharedata
}