I am using a service with an async call.
The service looks like that;
var routesApp = angular.module('routesApp', []);
routesApp.factory('angRoutes', function($http) {
var angRoutes = {
async: function(id) {
var data = $.param({
query: id
});
var config = {
headers : {
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8;'
}
}
var promise = $http.post('../ajax-php.php', data, config)
.success(function (data, status, headers, config) {
return data;
console.log(data);
})
.error(function (data, status, header, config) {
});
return promise;
}
};
return angRoutes;
});
When the page first load I use one controller to fill the scope;
routesApp.controller('topRoutesCtrl', function topRoutesCtrl($scope,$http, angRoutes) {
angRoutes.async('top').then(function(data) {
$scope.angRoutes = data;
console.log(data);
});
});
This is all working great. But then I use another controller for when the user click on something.
routesApp.controller('navRoutesCtrl', function navRoutesCtrl($scope,$http, angRoutes) {
$scope.update = function(id) {
angRoutes.async(id).then(function(data) {
$scope.angRoutes = data;
console.log(data);
});
}
I am able to see the data I am getting in the console and the id does get passed in the update function and the data is corect but it seams that my scope is not getting updated. it remains the value that was first sent when the page load.
How do I update my scope?
UPDATE
as seen here
In my angular HTML I do a ng-repeat like this
<div ng-controller="topRoutesCtrl" class="ajax">
<div id="ajaxLoader"> <img class="loadingGif" src =" /images/ajax-loader.gif"> </div>
<div data-ng-repeat="r in angRoutes.data" class="routes-block" >
{{r.name}}
</div>
</div>
Now in my angular JS if I do
routesApp.controller('topRoutesCtrl', function topRoutesCtrl($scope,$http, angRoutes) {
angRoutes.async('top').then(function(data) {
$scope.angRoutes = data;
console.log(angRoutes);
});
});
Note that in the console I can see the same thing if I console.log(data) or console.log(angRoutes) My app however will only work if I do $scope.angRoutes = data; and nothing gets displayed if I do $scope.angRoutes = angRoutes;
So maybe I am using the referencve the wrong way in my ng-repeat
you can use wrapper $timeout for manual start $digest cicle
$scope.update = function(id) {
angRoutes.async(id).then(function(data) {
$timeout(function() {
$scope.angRoutes = data;
}
console.log(data);
});
}
but keep in mind that $digest cicle are triggerd automaticly after $http calls and written behavior is strange
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 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!
As soon as Html page gets loaded, it calls SuperCategoryController, where i am assigning supercategories to $scope variable.
$scope.SuperCategories = SuperCategoryService.GetSuperCategories();
But as this controller is depends on service, which in turn calls the http request. so at the time pf assignment http request is not completed. so $scope.SuperCategories is getting assiged to undefined.
sampleApp.service('SuperCategoryService', ['$http', function ($http){
var URL = 'http://localhost/cgi-bin/superCategory.pl';
var SuperCategories;
$http({
method: 'POST',
url: URL,
data: "action=GET",
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).
success(function (data) {
alert (data);
if (data != null || data != 'undefined') {
SuperCategories = data;
}
})
.error(function (error) {
alert (error.message);
//$scope.status = 'Unable to retrieve super categories' + error.message;
});
//simply returns the SuperCategories list
this.GetSuperCategories = function () {
//alert (SuperCategories);
return SuperCategories;
}
}]);
sampleApp.controller('SuperCategoryController', ['$scope', 'SuperCategoryService', function ($scope, SuperCategoryService){
$scope.SuperCategories = SuperCategoryService.GetSuperCategories();
$scope.LoadSuperCategoryMapping = function()
{
alert ($scope.SelectedSuperCategory.id);
}
}]);
How to solve this problem in proper way.
I haven't tried this code myself but I would approach a solution using factory and a promise to make sure the data has been loaded. Something along these lines:
sampleApp.factory('SuperCategoryService', ['$http', function ($http){
return {
GetSuperCategories: function () {
var URL = 'http://localhost/cgi-bin/superCategory.pl';
return $http.get(URL);
}
}
}]);
sampleApp.controller('SuperCategoryController', ['$scope', 'SuperCategoryService', function ($scope, SuperCategoryService){
$scope.SuperCategories = function() {
SuperCategoryService.GetSuperCategories()
.then(function(d) {
if (d.data != undefined) {
// Your data should be loaded here
console.log(d.data);
$scope.SuperCategories = d.data;
}
})
.error(function(data, status) {
// Errors here
});
}
}]);
On my app, I need to recover data (json) by making multiples validations using http requests before all my app starts. So my problem is that I'm using angular.run() to make all the http requests and resolving all of the validations with promises.
The problem is, not all of my promises are executed before my app is started.
part of my code is:
appModule.run(configRun);
configRun.$inject = [
'$http', '$rootScope', 'gettextCatalog', 'ipLoadDataService',
'webStorageService', 'ipDataSetParserService'];
function configRun($http, $rootScope, gettextCatalog, ipLoadDataSrv, webStrSrv, dataSetParser) {
webStrSrv.clear();
ipLoadDataSrv.getHeadDataSet2()
.then(function (responseHead) {
if (ipLoadDataSrv.updatedDataSet2(responseHead.headers["last-modified"])) {
//save into localstorage
webStrSrv.clear();
webStrSrv.setItem("last-modified", { date: responseHead.headers["last-modified"] });
ipLoadDataSrv.getDataSet2()
.then(function (responseData) {
$rootScope.cabecera = responseData;
})
}
})
}
// LoadDataService
appModule.factory('ipLoadDataService', loadDataService);
loadDataService.$inject = ['$http',
'$q',
'webStorageService',
'myPrjEnvironment',
'ipDataSetParserService'];
function loadDataService($http, $q, webStoreService, myPrj, dataSetParser) {
var eventMap = [];
var ip_loadDataService = {
getHeadDataSet2: getHeadDataSet2,
requestDataSet: requestDataSet,
updatedDataSet2: updatedDataSet2,
getDataSet2: getDataSet2
};
return ip_loadDataService;
function getHeadDataSet2() {
/*HEAD*/
var deferred = $q.defer();
$http.head(myPrj.URL_DATA)
.success(function (data, status, headers, config) {
var response = [];
response.data = data;
response.headers = headers();
deferred.resolve(response);
//return deferred.promise;
}).error(function (data, status) {
deferred.reject(data);
});
return deferred.promise;
}
function getDataSet2() {
return xhr('get', [myPrj.URL_DATA]);
}
function updatedDataSet2(last_date_modified) {
//var self = this;
var dateOnWebStore = webStoreService.getItem("last-modified");
if (dateOnWebStore === null || Date.parse(dateOnWebStore.date) < Date.parse(last_date_modified))
return true;
return false;
}
function xhr(type, config) {
if (!config && angular.isArray(type)) {
config = type;
type = 'get';
}
var deferred = $q.defer();
$http[type].apply($http, config)
.success(function (data, status, headers, config) {
var response = [];
response.data = data;
response.headers = headers();
deferred.resolve(response);
})
.error(function (error) {
deferred.reject(error);
});
return deferred.promise;
}
}
Answering the question in your second post, maybe you better edit your original post with the new issue you encountered.
If what you are looking for is a way to activate a state (home.myPrjMain in your case) you can do this in various ways:
Using JS - use $state.go(). See - $State documentation
Using directive - use the ui-sref directive with the name of the required state. See - ui-sref documentation
Using regular html href (Navigate to url) - with the full address of the state you need. In your case, "/main".
I hope this was helpful
Have the UI start in an initial loading state then use ui-router to wait for the various pieces to resolve before going to the initial state.
Here is a fiddle showing how it works. Fiddle
I did two parts, one with a single fake service call using timeout and a second with a chained set of calls,.
this.slowServiceCall = function(input, delay) {
var deferred = $q.defer();
var workFinished = function () {
deferred.resolve(input);
};
$timeout(workFinished, delay);
return deferred.promise;
};
this.slowChainedServiceCall = function(input, delay) {
var deferred = $q.defer();
var workFinished = function () {
deferred.resolve(input);
};
$timeout(workFinished, delay);
var promiseChain = deferred.promise.then(function(result) {
var deferred2 = $q.defer();
$timeout(function(){
deferred2.resolve(result + ' Second Piece');
},100);
return deferred2.promise;
});
return promiseChain;
};
I am following init function in app.js
$scope.init = function ()
{
if(localStorage.getItem("id")!=null && localStorage.getItem("pass")!=null)
{
alert(localStorage.getItem("id")+" "+localStorage.getItem("pass"));
var id=localStorage.getItem("id");
var pass=localStorage.getItem("pass");
$http({method: 'GET', url: site+'/login-web.php?txt_email='+id+'&txt_password='+pass}).
success(function(data, status, headers, config)
{
if(data=='error')
navigator.notification.alert("Wrong username or password.",null,"Attention.!","Try Again.!");
else
{
localStorage.setItem("id", id);
localStorage.setItem("pass", password);
alert("fire");
$scope.ons.navigator.pushPage('dashboard.html',{title : 'title'});
}
}).
error(function(data, status, headers, config)
{
alert("Please check Mobile Data.");
// called asynchronously if an error occurs
// or server returns response with an error status.
});
}
},
and i am fire init from
<body ng-controller="AppController" ng-init="init()">
I want to like this if i am login first time then i need to login and i store id and pass in localstorage and every time when application is load and init method is fire and i check id and pass from localstorage and fire server method to check id and pass if its right login is done automatically.
I am using phonegap + onsenui + angular js.
Problem is from init method
$scope.ons.navigator.pushPage('dashboard.html',{title : 'title'});
is not redirect to dashborad.
There is nothing wrong with $scope.ons.navigator.pushPage('dashboard.html',{title : 'title'});. But I think inside your <body> tag, there is no <ons-navigator> tag which is needed in order to use that function. Moreover, you need to call the function inside a setTimeout otherwise it will be called before the DOM element finishes rendering. Do as follows:
Here is what you need to add to ur controller:
app.factory('DataService', function($http) {
var service = {
requestData: function(url) {
return $http.get(url).then(function(data, status, headers, config) {
service.myData = data;
return service.myData;
});
},
myData: null,
return service;
});
app.controller('AppController', function($scope, DataService)){
$scope.init = function(){ setTimeout($scope.init_wait, 10); };
$scope.init_wait = function () {
if(localStorage.getItem("id")!=null && localStorage.getItem("pass")!=null){
alert(localStorage.getItem("id")+" "+localStorage.getItem("pass"));
var id=localStorage.getItem("id");
var pass=localStorage.getItem("pass");
var url = ite+'/login-web.php?txt_email='+id+'&txt_password='+pass;
DataService.requestData(url).then(function(data, status, headers, config) {
if(data=='error')
navigator.notification.alert("Wrong username or password.",null,"Attention.!","Try Again.!");
else {
localStorage.setItem("id", id);
localStorage.setItem("pass", password);
alert("fire");
$scope.ons.navigator.pushPage('dashboard.html',{title : 'title'});
}
});
}
}
};
Here inside your HTML
<body ng-controller="AppController" ng-init="init()">
<ons-navigator>
<!--Your HTML-->
</ons-navigator>
</body>