I have a provider for my REST-Services named MyRestServices:
app.provider('MyRestServices', function() {
this.baseUrl = null;
this.setBaseUrl = function(_baseUrl) {
this.baseUrl = _baseUrl;
};
this.$get = ['$http', function($http) {
var _baseUrl = this.baseUrl;
function getMyData() {
return $http.get(_baseUrl + 'data1/?token=' + token + '&key=' + key);
}
function preGetTokenAndKey() {
return $http.get(_baseUrl + 'keyAndToken/');
}
return {
getMyData: getMyData,
preGetTokenAndKey: preGetTokenAndKey
};
}];
});
I configure it before calling the first REST service.
app.config(function(MyRestServicesProvider) {
MyRestServicesProvider.setBaseUrl('https://www.test.com/rest/');
});
And then I have a HeadCtrl controller which should call preGetTokenAndKey to get key and token which is needed for some other REST calls like getMyData.
app.controller('HeadCtrl', function (MyRestServices) {
MyRestServices.preGetTokenAndKey().success(function(data) {
var key = data.dataSection.key;
var token = data.dataSection.token;
});
});
My problem is I want to call getMyData from another controller, but I need key and token to make this call.
So I need to wait until preGetTokenAndKey was successful and I have to provide the two values to the MyRestServices provider.
How can I solve these problems?
It sounds like a better solution would be to chain them within your service itself. You'd setup your own promise within preGetTokenAndKey, which gets resolved by the $http call. Subsequent calls to preGetTokenAndKey() would just return the already resolved data w/o making additional $http calls.
So something along the lines of the following should get you started:
app.provider('MyRestServices', function() {
this.baseUrl = null;
this.setBaseUrl = function(_baseUrl) {
this.baseUrl = _baseUrl;
};
this.$get = ['$http', function($http) {
var _baseUrl = this.baseUrl;
var _tokenAndKey = {};
function getMyData() {
return preGetTokenAndKey().then(function (data) {
return $http.get(_baseUrl + 'data1/?token=' + data.dataSection.token + '&key=' + data.dataSection.key);
});
}
function preGetTokenAndKey() {
if(!_tokenAndKey.set) {
_tokenAndKey.deferred = $http.get(_baseUrl + 'keyAndToken/').then(function(data) {
_tokenAndKey.set = true;
return data;
});
}
return _tokenAndKey.deferred.promise;
}
return {
getMyData: getMyData,
preGetTokenAndKey: preGetTokenAndKey
};
}];
});
My problem is I want to call getMyData from another controller,
If so, you can use $broadcast to notify other controller that async call resolved and you have key/token
app.controller('HeadCtrl', function($rootScope, MyRestServices) {
MyRestServices.preGetTokenAndKey().success(function(data) {
var key = data.dataSection.key;
var token = data.dataSection.token;
$rootScope.$broadcast("getMyDataTrigger", {key: key,token: token});
});
});
In other controller implement listener:
$rootScope.$on("getMyDataTrigger", function(event, data){
if(data){
MyRestServices.getMyData(data.key, data.token);
// ...
}
});
Just override getMyData:
function getMyData(key, token) {
return $http.get(_baseUrl + 'data1/?token=' + token + '&key=' + key);
}
Related
I have been searching for an answer to this, and cannot seem to find anything. I have a service, in the first block I am successfully logging a url that I then need to pass into my getData() function. But it comes back undefined, I have tried the method below, and I tried moving the first $http.get into the controller where I am calling it, as well as moving the first $http.get into the getData() function. Am I going about this all wrong?
di.service('testService', function($http) {
$http.get('https://us.api.data/tichondrius?locale=en_US&apikey=xxxxxxxx').
then(function(response) {
var urlToJsonFileUncut = response.data.files[0].url;
console.log(urlToJsonFileUncut);
urlToJsonFile = urlToJsonFileUncut.slice(7);
console.log(urlToJsonFile);
return urlToJsonFile;
});
this.getData = function(urlToJsonFile) {
console.log(urlToJsonFile);
return $http.get('http://localhost:1337/' + urlToJsonFile).
then(function(response) {
console.log(response.data.realms[0].name);
return response.data.realms[0].name;
});
}});
$http is an async request. so you need to chain it inside the first request to ensure the value of first response is available when second request is called.
di.service('testService', function($http) {
var getData = function () {
return $http.get('https://us.api.data/tichondrius?locale=en_US&apikey=xxxxxxxx').
then(function(response) {
var urlToJsonFileUncut = response.data.files[0].url;
console.log(urlToJsonFileUncut);
var urlToJsonFile = urlToJsonFileUncut.slice(7);
console.log(urlToJsonFile);
$http.get('http://localhost:1337/' + urlToJsonFile).
then(function(response) {
console.log(response.data.realms[0].name);
return response.data.realms[0].name;
});
});
}
return { getData: getData; }
});
I would suggest you to use a factory instead of a service
Check out the below code
di.factory('testService', function ($http) {
var variable_name;
var serviceMethodName = function () {
$http.get('https://us.api.data/tichondrius?locale=en_US&apikey=xxxxxxxx').
then(function (response) {
var urlToJsonFileUncut = response.data.files[0].url;
console.log(urlToJsonFileUncut);
urlToJsonFile = urlToJsonFileUncut.slice(7);
console.log(urlToJsonFile);
variable_name = urlToJsonFile; //added
});
}
//modified parameter in below method
var getData = function (variable_name) {
var urlToJsonFile = variable_name; //added
console.log(urlToJsonFile);
return $http.get('http://localhost:1337/' + urlToJsonFile).
then(function (response) {
console.log(response.data.realms[0].name);
return response.data.realms[0].name;
});
}
//Exposes the two methods and accessbile through out the app unless it is modified
return {
serviceMethodName: serviceMethodName,
getData:getData
}
});
In my Angular controller I have a http call to a REST service that returns data in a database. This data are shown in a table in the partial view.
It happens random that the render of the html view is done before to get the callback with data, so I see a void table.
Here the code in the controller (I use services for some business logic and to implement the http call):
commonServices.find(vm.modelUri, null, vm.filter, function (err, msg, data) {
if (err || !data.length) {
$scope.noResults = true;
return;
}
$scope.docs = data; //docs is bind in the view
return;
});
Here the service for the http call:
function _commonServices(config, $http, $rootScope, $cookies) {
return {
find: function _find(modelUri, id, filter, callback) {
var url = config.servicesUri + '/' + modelUri;
if (id) {
url += '/' + id;
}
if (filter) {
if (typeof filter !== 'string') {
filter = JSON.stringify(filter);
}
url += '?filter=' + filter;
if (document.cookie) {
url += '&' + accessToken;
}
} else {
if (document.cookie) {
url += '?' + accessToken;
}
}
$http.get(url)
.then(function (data) {
//success
return callback(null, null, data.data);
},
function (data) {
//error
var err = true;
return callback(err, data.data.error.message);
});
}
}
}
The find service is used in other controllers and it seems it works good. I would know if it is possible to defer the render of the html table until the data are ready to be shown.
I would suggest the use of Angular's ng-cloak. It is a directive to prevent the html from being displayed until your angular app is finished loading. Check out the documentation here: ng-cloak
I have a problem with my Angular Service. I have two controllers and one service. Basically the first controller gets data via AJAX call and store that data on the service. The second controller then access that data via service. I have successfully passed the data from the 1st controller to the service however, when I access the data from the 2nd controller it returns nothing.
I have one view with 2 controllers by the way.
Thanks
Service
app.service('SharedDataService', function () {
// Holds subtask that will be passed to other controllers
// if SharedDataService is invoke.
var _subTask = {};
return {
subTask : _subTask
};
});
Controller 1
app.controller('MainCategoryController',function($scope,$http,SharedDataService){
$scope.loadSubtask = function(m_uid){
$http({
method: 'POST',
url: $locationProvider + 'query_stasks',
data: {
m_uid: m_uid
}
}).then(function successCallback(response) {
SharedDataService.subTask = response.data;
},function errorCallback(response){
});
}
}
Controller 2
app.controller('SubTaskController',function($scope,$http,$location,$rootScope,SharedDataService){
$scope.$watch('SharedDataService.subTask', function(newValue,oldValue){
console.log("ni sud');");
if (newValue !== oldValue) {
$scope.subTasks = newValue;
}
return SharedDataService.subTask;
});
}
Because SharedDataService is not on $scope, the first argument of the $watch method needs to be a watchExpression function instead of an Angular expression string.
app.controller('SubTaskController',function($scope,$http,$location,$rootScope,SharedDataService){
$scope.$watch(
function watchExpression() {return SharedDataService.subTask},
function listener (newValue,oldValue){
console.log("ni sud");
if (newValue !== oldValue) {
$scope.subTasks = newValue;
}
}
});
});
For more information, see AngularJS $rootScope.scope API Reference - $watch.
May be you should save the value use object in service.In my project, i always do like this:
app.service('SharedDataService', function () {
var service = {};
service._subTask = '';
service.toogle_sub_task = function(v){
if(v){
service._subTask = v;
}
return service._subTask;
}
return service;
});
Then, in your controller. You should call service.toogle_sub_task to set and get value. Just give it a try.Best wishes.
you have to write a method to store the data into service and return the data from service
app.factory('SharedDataService', function () {
// Holds subtask that will be passed to other controllers
// if SharedDataService is invoke.
var _subTask = [];
function setSubTask = function(data){
_subTask = data;
};
return {
subTask : _subTask,
setSubTask:setSubTask
};
});
and in controller call
SharedDataService.setSubTask(response.data);
to set the data...
try it
app.service('SharedDataService', function () {
this._subTask ={};
});
// keep code same 1st controller
app.controller('MainCategoryController',function($scope,$http,SharedDataService){
$scope.loadSubtask = function(m_uid){
$http({
method: 'POST',
url: $locationProvider + 'query_stasks',
data: {
m_uid: m_uid
}
}).then(function successCallback(response) {
SharedDataService._subTask = response.data;
},function errorCallback(response){
});
}
}
// 2nd controller
app.controller('SubTaskController',function($scope,$http,$location,$rootScope,SharedDataService){
console.log(SharedDataService._subTask );
});
The best practice is to make any $http/resource calls in services and not in controller or directives directly. This makes the code more module and less interlinked.
You should ideally have
app.factory('SharedDataService', function ($http) {
var subtasks = [];
var saveSubtasks = function(sub){
subtasks = sub;
console.log(subtasks)
}
var tasks = {
loadSubtasks : function(m_uid){
$http({
method: 'POST',
url: $locationProvider + 'query_stasks',
data: {
m_uid: m_uid
}
}).then(function(data){
saveSubtasks(data);
});
},
getSubtasks : function(){
return subtasks;
}
}
return tasks;
});
and use it like
app.controller('SharedDataService',function($scope,SharedDataService){
$scope.load = function(val){
SharedDataService.loadSubtasks(val);
}
});
app.controller('SubTaskController',function($scope,SharedDataService){
$scope.get = function(){
$scope.subtasks = SharedDataService.getSubtasks();
console.log($scope.subtasks);
}
});
I'm really struggling with this because it should be very simple. I have a route with a controller defined called login. In my template I have the following data binding {{error}} which is defined in my controller after executing a method from a custom service, and resolving the returned promise.
Controller
app.controller("login", ['$scope','XMLMC', 'ManageSettings', function ($scope,api,ManageSettings) {
$scope.error = 'test';
$scope.login = function() {
var params = {
selfServiceInstance: "selfservice",
customerId: $scope.username,
password: $scope.password
};
var authenticated = api.request("session","selfServiceLogon",params).then(function(response) {
ManageSettings.set("session",response, $scope);
if(response.status === "ok") {
window.location.href = 'portal';
} else {
$scope.error = response["ERROR"];
console.log($scope.error);
}
});
};
}]);
The console shows Customer not registered. Showing that $scope.error has been updated appropriately, but the view never gets updated. My service is below, and please note that I am doing nothing "outside" of angular and so I should not have to $apply() anything manually.
app.factory("XMLMC", ['$http', '$q', function ($http, $q) {
function XMLMC($http, $q) {
$http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
var that= this;
this.prepareForPost = function(pkg) {
return JSON.stringify(pkg);
};
this.request = function(service, request, params, host, newsession) {
var def = $q.defer();
var P = def.promise;
if(request === "analystLogon") {
newsession = true;
}
var call = {
service: service,
method: request,
params: params
};
if(host) {
call.host = host;
} else {
call.host = "localhost";
}
if(newsession) {
call.newsession = "true";
}
var pkg = {
contents: this.prepareForPost(call)
};
$http.post('php/XMLMC/api.php', jQuery.param(pkg)).success(function (response,status) {
that.consume(response, def);
}).error(function (response,status) {
def.reject(response,status);
});
return P;
};
this.consume = function(response, defer) {
console.log(response);
var resp = response[0],
digested = {},
i;
digested.status = resp["attrs"]["STATUS"];
var params = resp["children"][0]["children"];
for(i=0; i < params.length; i++) {
var key = params[i]["name"];
var val = params[i]["tagData"];
digested[key] = val;
}
defer.resolve(digested);
//return digested;
};
}
return new XMLMC($http, $q);
}]);
I've created a plunk here with the code exactly as it is on my test server. The routes and etc aren't working for obvious reasons, but you can at least see the code and how it works together
http://plnkr.co/edit/AodFJfCijsp2VWxWpbR8?p=preview
And here is a further simplified plunk where everything has one scope and one controller and no routes. For some reason, this works in the plunk but the $http method fails in my server
http://plnkr.co/edit/nU4drGtpwQwFoBYBfuw8?p=preview
EDIT
Even this fails to update
var authenticated = api.request("session","selfServiceLogon",params).then(function(response) {
ManageSettings.set("session",response, $scope);
$scope.error = "foo!";
if(response.status === "ok") {
window.location.href = 'portal';
}
});
It appears that $scope.$apply is indeed needed. See AngularJS - why is $apply required to properly resolve a $q promise?
To quote #pkozlowski.opensource:
In AngularJS the results of promise resolution are propagated asynchronously, inside a $digest cycle. So, callbacks registered with then() will only be called upon entering a $digest cycle.
I'm new to AngularJS and am still trying to wrap my head around using services to pull data into my application.
I am looking for a way to cache the result of a $http.get() which will be a JSON array. In this case, it is a static list of events:
[{ id: 1, name: "First Event"}, { id: 2, name: "Second Event"},...]
I have a service that I am trying to use to cache these results:
appServices.service("eventListService", function($http) {
var eventListCache;
this.get = function (ignoreCache) {
if (ignoreCache || !eventListCache) {
eventListCache = $http.get("/events.json", {cache: true});
}
return eventListCache;
}
});
Now from what I can understand I am returning a "promise" from the $http.get function, which in my controller I add in a success callback:
appControllers.controller("EventListCtrl", ["$scope", "eventListService",
function ($scope, eventListService) {
eventListService.get().success(function (data) { $scope.events = data; });
}
]);
This is working fine for me. What I'd like to do is add an event to the eventListService to pull out a specific event object from eventListCache.
appServices.service("eventListService", function($http) {
var eventListCache;
this.get = function (ignoreCache) { ... }
//added
this.getEvent = function (id) {
//TODO: add some sort of call to this.get() in order to make sure the
//eventListCache is there... stumped
}
});
I do not know if this is the best way to approach caching or if this is a stupid thing to do, but I am trying to get a single object from an array that may or may not be cached. OR maybe I'm supposed to call the original event and pull the object out of the resulting array in the controller.
You're on the right track. Services in Angularjs are singeltons, so using it to cache your $http request is fine. If you want to expose several functions in your service I would do something like this. I used the $q promise/deferred service implementation in Angularjs to handle the asynchronus http request.
appServices.service("eventListService", function($http, $q) {
var eventListCache;
var get = function (callback) {
$http({method: "GET", url: "/events.json"}).
success(function(data, status) {
eventListCache = data;
return callback(eventListCache);
}).
}
}
return {
getEventList : function(callback) {
if(eventListCache.length > 0) {
return callback(eventListCache);
} else {
var deferred = $q.defer();
get(function(data) {
deferred.resolve(data);
}
deferred.promise.then(function(res) {
return callback(res);
});
}
},
getSpecificEvent: function(id, callback) {
// Same as in getEventList(), but with a filter or sorting of the array
// ...
// return callback(....);
}
}
});
Now, in your controller, all you have to do is this;
appControllers.controller("EventListCtrl", ["$scope", "eventListService",
function ($scope, eventListService) {
// First time your controller runs, it will send http-request, second time it
// will use the cached variable
eventListService.getEventList(function(eventlist) {
$scope.myEventList = eventlist;
});
eventListService.getSpecificEvent($scope.someEventID, function(event) {
// This one is cached, and fetched from local variable in service
$scope.mySpecificEvent = event;
});
}
]);
You are on the right track. Here's a little help:
appServices.service("eventListService", function($http, $q) {
var eventListCache = [];
function getList(forceReload) {
var defObj = $q.defer(), listHolder;
if (eventListCache.length || forceReload) {
listHolder= $http.get("/events.json", {cache: true});
listHolder.then(function(data){
eventListCache = data;
defObj.resolve(eventListCache);
});
} else {
defObj.resolve(eventListCache);
}
return defObj.promise;
}
function getDetails(eventId){
var defObj = $q.defer();
if(eventId === undefined){
throw new Error('Event Id is Required.');
}
if(eventListCache.length === 0){
defObj.reject('No Events Loaded.');
} else {
defObj.resolve(eventListCache[eventId]);
}
return defObj.promise;
}
return {
eventList:getList,
eventDetails:getDetails
};
});
Then, in your controller, you handle it like this:
appControllers.controller("EventListCtrl", ["$scope", "eventListService",
function ($scope, eventListService) {
var eventList = eventListService.getList();
eventList.then(function(data){
$scope.events = data;
});
$scope.getEventsList = function(reloadList){
eventList = eventListService.getList(reloadList);
eventList.then(function(data){
$scope.events = data;
});
};
$scope.getEventDetails = function(eventID){
var detailsPromise = eventListService.getDetails(eventID);
detailsPromise.then(function(data){
$scope.eventDetails = data;
}, function(reason){
window.alert(reason);
});
}
}
]);
This way, your events are loaded when the controller first loads, and then you have the option to request a new list by simply passing in a boolean. Getting event details is also handled by an internal promise to give you some error handling without throwing a disruptive error.