I've a factory named as 'userService'.
.factory('userService', function($http) {
var users = [];
return {
getUsers: function(){
return $http.get("https://www.yoursite.com/users").then(function(response){
users = response;
return users;
});
},
getUser: function(index){
return users[i];
}
}
})
In the first page, On button click I want to call getUsers function and it will return the 'users' array.
I want to use 'users' array in the second page. How can I do it?
P.s: I'm using getters and setters to store the response in first page and access the same in second page. Is this the way everyone doing?
1). getUsers. For consistence sake I would still use the same service method on the second page but I would also add data caching logic:
.factory('userService', function($q, $http) {
var users;
return {
getUsers: function() {
return users ? $q.when(users) : $http.get("https://www.yoursite.com/users").then(function(response) {
users = response;
return users;
});
},
getUser: function(index){
return users[i];
}
};
});
Now on the second page usage is the same as it was on the first:
userService.getUsers().then(function(users) {
$scope.users = users;
});
but this promise will resolve immediately because users are already loaded and available.
2). getUser. Also it makes sense to turn getUser method into asynchronous as well:
getUser: function(index){
return this.getUsers().then(function(users) {
return users[i];
});
}
and use it this way in controller:
userService.getUser(123).then(function(user) {
console.log(user);
});
Here is the pattern i have followed in my own project. You can see the code below
.factory('userService', function($http) {
return {
serviceCall : function(urls, successCallBack) {
$http({
method : 'post',
url : url,
timeout : timeout
}).success(function(data, status, headers, config) {
commonDataFactory.setResponse(data);
}).error(function(data, status, headers, config) {
commonDataFactory.setResponse({"data":"undefined"});
alert("error");
});
}
},
};
After service call set the response data in common data factory so that it will be accessible throughout the app
In the above code I've used common data factory to store the response.
Related
I am retrieving json data form a url, and if i output to console the data is there.. but when i am injecting it to the controller its not working. What did i do wrong?
angular.module('starter.notifications', [])
.factory('Notifications', function($http) {
return {
getAll: function()
{
return $http.get(link).then(function(response){
console.log(response.data);
notifications = response.data;
return notifications;
});
}
}
My controller
.controller('NotificationsCtrl', function($scope, $state, Notifications) {
$scope.notifications = Notifications.getAll();
})
$scope.notifications is null. So i don't understand why thats not working.
UPDATE:
So i now have it passing the data.. but i guess i don't understand how to use it.
Correct/Working code
getAll: function()
{
notifications = ($http.get(link).then(function(response){ return response.data}));
return notifications;
}
So now in my controller when i do
console.log(notifications);
i get this
So how do i use that data? the array of data i want is there... but i can't get it. I thought i could use
notifications.value but that doesn't work
I think the error is that your are sending two return statements in your factory.Change your code to
getAll: function()
{
var temp = $http.get(link).then(function(response){
console.log(response.data);
var notifications = response.data;
return notifications;
});
}
notifications = ($http.get(link)
.success(function(data, status, headers, config){
console.log(data);
return data;
})
.error(function(err) {
return err;
})
);
return notifications;
need to pass more than a single parameter. Originally i was only passing response vice data,status, headers, config
I have following controller
1) introCtrl
2) ArticleCtrl
3) articleService (Service)
Now I am sending an http request from introCrtl
.controller('IntroCtrl', function($scope, articleService) {
articleService.getArticles();
});
and AricleCtrl is
.controller('ArticleCtrl', function($scope,$rootScope,articleService) {
$scope.articles = articleService.fetchArticles();
})
and my Service is
.service('articleService', function ($http, $q) {
var articleList = [];
var getArticles = function() {
$http({
url: "muylink,co,",
data: { starLimit: 0, endLimit: 150,created_date: 0 },
method: 'POST',
withCredentials: true,
}).success(function (data, status, headers, config) {
articleList.push(data);
}).error(function (err) {
console.log(err);
})
};
var fetchArticles = function() {
return articleList[0];
}
return {
getArticles: getArticles,
fetchArticles: fetchArticles
};
});
Which is also working fine. Now Problem is that
Sometimes my http request sending respone late and i got nothing in
$scope.articles.
Can we implement watch here. How i need to implement $watch here. I dont want to implement promise. because i want to run http request behind the scene.
Thanks
It would be better if you switch to a state based setup with ui-router that way you can do this :
$stateProvider.state('myState', {
url: 'the/url/you/want',
resolve:{
articleService: 'articleService' // you are dependency injecting it here,
articles: function (articleService) {
return articleService.getArticles.$promise;
}
},
controller: 'IntroCtrl'
})
// then your controller can just inject the articles and they will be resolved before your controller loads so you it will always be fetched prior
.controller('IntroCtrl', function($scope, articles) {
$scope.articles = articles;
});
for more information take a look at this
ui-router info
All to do is set watch on articleList and provide maintaining function.
As you are watching array, it's good to change it to string.
Create function in watch which results array.
$scope.$watch( function() {
return JSON.stringify($scope.articleList);
}, function(newVal,oldVal){
//provide logic here
});
If your service result is asynchron (like http requests) you should return promises from your service.
.controller('ArticleCtrl', function($scope,$rootScope,articleService) {
articleService.fetchArticles().then(function(articles) {
$scope.articles = articles;
});
})
Service
// not sure about your service logic... simplified:
.service('articleService', function ($http, $q) {
var articleListPromise ;
var getArticles = function() {
articleListPromise = $http(/* ...*/);
};
var fetchArticles = function() {
return articleListPromise.then(function(data) {
return data[0];
});
}
return {
getArticles: getArticles,
fetchArticles: fetchArticles
};
});
I need to grab some data from my db through an API and make it accessible throughout my Angular app. I understand that Services are good for storing data to be accessed from multiple controllers. However, in the following code I end up with a new $hhtp.get() each time just to get the same data.
Service:
.factory('Playlist', ['$http', function($http) {
var playlist = {};
playlist.getPlaylist = function() {
return $http.get('api/playlist.php')
.then(function (response) {
var data = response.data;
return data;
})
}
return playlist;
}])
Controllers:
.controller('ScheduleCtrl', ['$http', 'Playlist', function($http, Playlist) {
var self = this;
Playlist.getPlaylist()
.success(function(playlist) {
self.playlist_id = playlist.id;
fetchItems();
})
var fetchScheduleItems = function() {
return $http.get('api/schedule.php/'+self.playlist_id).then(
function(response) {
if (response.data === "null") {
console.log("No items");
} else {
self.items = response.data;
}
}, function(errResponse) {
console.error('Error while fetching schedule');
});
};
}])
.controller('PlaylistItemCtrl', ['$http', 'Playlist', function($http, Playlist) {
var self = this;
Playlist.getPlaylist()
.success(function(playlist) {
self.playlist_id = playlist.id;
fetchItems();
})
var fetchPlaylistItems = function() {
return $http.get('api/schedule.php/'+self.playlist_id).then(
function(response) {
if (response.data === "null") {
console.log("No items");
} else {
self.items = response.data;
}
}, function(errResponse) {
console.error('Error while fetching schedule');
});
};
}])
Is there a way to store the Playlist ID without pinging 'api/playlist.php' from every controller?
Update
Here's a Plunkr based on Abhi's answer: http://plnkr.co/edit/or9kc4MDC2x3GzG2dNeK?p=preview
As you can see in the console, it's still hitting the server several times. I've tried nesting CachedData.save() differently, but it doesn't seem to apply.
I would say store your data locally (CachedData factory - rename it to something that makes sense) and inside your getPlaylist method, before doing http call, check CachedData to see if your data is present and if not, then do the http call.
The code will be something like the below. I have just written it free-hand, so there may be some errors, but you get the picture.
.factory('Playlist', ['$http', 'CachedData', function($http, CachedData) {
var playlist = {};
playlist.getPlaylist = function() {
if (CachedData.data) {
// return cached data as a resolved promise
} else
return $http.get('api/playlist.php')
.then(function (response) {
var data = response.data;
cachedData.save(data);
return data;
})
}
return playlist;
}])
// CachedData factory
.factory('CachedData', function() {
var _data;
var cachedData = {
data: _data,
save: function(newData) {
_data = newData;
}
};
return cachedData;
})
EDIT: Also Remove fetchPlaylistItems from the controller and put it in a factory. The controller is just a glue between your viewmodel and view. Put all your business logic, http calls in a service.
EDIT: I have setup a plunk for you here. I hope it helps.
EDIT: John, the reason you are seeing two server calls is because they are from different controllers ManiCtrl1 and MainCtrl2. By the time, the getPlaylist method from MainCtrl2 is called, the first http request didn't get the chance to finish and save the data. If you add a timeout to MainCtrl2 before calling your service method, you will see that the data is retrieved from cache. See this updated plunk for a demo.
This will be more useful in an app with multiple views, where you don't want to reload data when navigating back to a view. (Ideally, depending on the type of data you are caching, you will have some expiry time after which you would want to reload your data).
You could do some pre validation when calling that method.
var playlist = {};
playlist.playlist = [];
playlist.getPlaylist = function () {
if (playlist.playlist.length <= 0) { //or some lodash _.isEmpty()
$http.get('api/playlist.php')
.then(function (response) {
playlist.playlist = response.data;
})
}
return playlist.playlist;
};
Hope it helps!
I have multiple controllers on a page that use the same service, for the sake of example we will call the service USER.
The first time the USER.getUser() is called it does an $http request to GET data on the user. After the call is completed it stores the data in USER.data. If another call is made to USER.getUser() it checks if there is data in USER.data and if there is data it returns that instead of making the call.
My problem is that the calls to USER.getUser() happen so quickly that USER.data does not have any data so it fires the $http call again.
Here is what I have for the user factory right now:
.factory("user", function($http, $q){
return {
getUser: function(){
var that = this;
var deferred = $q.defer();
if(that.data){
deferred.resolve(that.data);
} else {
$http.get("/my/url")
.success(function(res){
that.data = res;
deferred.resolve(that.data);
});
}
return deferred.promise;
}
}
});
I hope my question makes sense. Any help would be much appreciated.
Does this work for you?
.factory("user", function($http, $q) {
var userPromise;
return {
getUser: function () {
if (userPromise) {
return userPromise;
}
userPromise = $http
.get("/my/url")
.then(function(res) {
return res.data;
});
return userPromise;
}
}
})
$http already returns the promise for you, even more, in 2 useful types: success & error. So basically, the option that #kfis offered does NOT catch errors and not flexible.
You could write something simple:
.factory('user', function($http) {
return {
getUser: function() {
$http.get('/my/url/')
.success(function(data) {
return data;
})
.error(function(err) {
// error handler
})
}
}
})
I have a service in angular JS which is defined as follows:
'use strict';
angular.module('Offering.AccountService', [])
.service('Account', function($http, config) {
var account = this;
account.accountDetails = {};
this.getAccountDetails = function(){
var request = $http({
method: 'GET',
url: config.accountURL,
headers: {
'X-Parse-Application-Id': config.applicationID,
'X-Parse-REST-API-Key': config.restAPIKey,
}
})
.success(function(data, status, headers, config) {
console.log('Successfully aqquired account details');
account.accountDetails = data.results[0];
})
.error(function(data, status, headers, config) {
console.warn(status);
});
}
});
This service calls out to an endpoint to retrieve some data, I am calling this method in the run function of the main module as is shown below:
'use strict';
angular.module('Offering', [
'routerRoutes'
])
.run(function(Account){
Account.getAccountDetails();
})
In my controller, I have a variable called balance, in which I want to store the value retrieved from the service, as is shown in the code below:
'use strict';
angular.module('Offering.HomeController', [])
.controller('HomeCtrl', function(Account) {
this.data = {
message: "Account Details",
updateBalance: function() {
Account.getAccountDetails()
},
balance: Account.accountDetails.balance
}
// this.$watch(
// function () { return Account.accountDetails; },
// function (data) {
// $scope.data.balance = data.balance
// },
// true
// );
})
The data from the service is returned after the controller data object has been set up and thus the balance field is not getting populated. If I use the $scope.watch approach that is commented out, then the data object gets populated properly. However, I was under the assumption that this need not be done as there is a two way data binding between the service and the controller. Any help is greatly appreciated. Thanks !
The .run function doesn't stop to wait for the getAccountDetails to return a value. And so, when you assign this.data.balance = Account.accountDetails.balance, you are essentially doing this: this.data.balance = undefined.
As per comment above, the typical way is to have getAccountDetails() to return a promise, like so:
this.getAccountDetails = function(){
return $http({...})
.then(function(response){
console.log("successfully returned balanace", response.data);
return response.data;
});
}
Then, in the controller:
var vm = this;
Account.getAccountDetails()
.then(function(accountDetails){
vm.balance = accountDetails.balance;
});
You can not put a watch on the function which is returning promise, better you could put watch on this.data, Their is different way of put watch while using this keyword inside controller.
CODE
$scope.$watch(angular.bind(this, function () {
return this.data;
}), function (newVal, oldVal) {
// now we will pickup changes to newVal and oldVal
});
Hope this could help you , Thanks.