Hello guys I really need help and advice on this factory and controller issue I am having.
I have a factory that gets data from the server
sp.factory('homeFeed',['$window','$http','auth',function($window,$http,auth){
var url = auth.url;
var version = auth.version;
var HomeFeed = {};
HomeFeed.getFeeds = function(user){
//setting variable for get request on home feed
var req = {
method: 'GET',
url: url + version + '/Feed',
headers: {
'Content-Type': 'application/json; charset=utf-8',
'Authorization': 'Bearer ' + token
},
}
return $http(req).success(function(res){
return res;
});
};
return HomeFeed;
}]);
controller--
sp.controller('HomeCtrl',['$scope','homeFeed','$window',function($scope,homeFeed,$window){
//getting all the home feed data
$scope.feeds = homeFeed.getFeeds(JSON.parse($window.localStorage['SP-User']))
}]);
however, after the respond from the server, my view is not updated and the $scope.feeds is not updated as well. Greatly appreciate your help
As you are doing async $http call then that data would not be available at that instance of time. It would be available when ajax call succeeded. You need to use .then function which will create a promise chain and will execute a function when .success function returns a data.
Controller
sp.controller('HomeCtrl',['$scope','homeFeed','$window',
function($scope,homeFeed,$window){
//getting all the home feed data
homeFeed.getFeeds(JSON.parse($window.localStorage['SP-User']))
.then(function(data){
$scope.feeds = data
});
}
]);
Related
Background
I am making Service which has a list to be shared between two controllers. To start I followed this tutorial on Services:
https://thinkster.io/a-better-way-to-learn-angularjs/services
And I managed to successfully create and execute the basic tutorial on Plunker:
https://plnkr.co/edit/niUlaHP54wWpjSoWURNX
Problem
The problem here, is that when I click the form button, I need to make an HTTP GET request to my server, and update the service list when I get the response.
To achieve this, I first tried using the following Plunker modification:
https://plnkr.co/edit/Z7K9CJbNP9LClycRHwBd?p=info
The jest of the code can be seen in the service:
// Create the factory that share the Fact
app.factory('ListService', function($http) {
var list = {};
list.data = [];
list.request = function(theHairColor) {
var theUrl = "https://gnome-shop-fl4m3ph03n1x.c9users.io/api/v1/gnomes?hairColor=" + theHairColor;
console.log(theUrl);
$http({
method: 'GET',
url: theUrl,
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
}).then(function successCallback(response) {
list.data = response.data.entries; //does not work
console.log(response.data.entries);
}, function errorCallback(response) {
console.log('Error: ' + response);
});
};
return list;
});
If you tried it out, you will see it simply doesn't work, and I don't really understand why. In a previous question I made, someone explained to me that it was related to references, and that I should replace list.data = response.data.entries; //does not work for the following:
//this works but is rather flimsy ....
list.data.length = 0;
Object.assign(list.data, response.data.entries);
Which in deed does work, but I find it rather counter intuitive:
https://plnkr.co/edit/ABdxPI4coNYlJ85EIOJl
Another suggestion was also given, in that I should change my gnomeList controller to :
app.controller("gnomeList", function(ListService) {
var self = this;
self.listService = ListService;
});
and then iterate over the service's list directly:
<div ng-controller="gnomeList as listCtrl">
<p ng-repeat="gnome in listCtrl.listService.data">{{ gnome.id }}: {{ gnome.name }}</p>
</div>
Which also works, but attaches the controller directly to the service:
https://plnkr.co/edit/h48CmupRjRFoxFT5ZSd8
Questions:
Are there any other ways to make my first code sample (that didn't work) work?
Which of these solutions would be preferable and why? (which one is more Angulary?)
Problem is you initially copy the data in your gnomeList and it is passed by value.
app.controller("gnomeList", function(ListService) {
var self = this;
self.list = ListService.data;
});
When your controller gets initialized here, it puts a copy of ListService.data into self.list. However, when updating the values in the services, this controller does not get initialized again and therefore the value is not updated.
Objects in javascript are passed by reference. Just like you said, you could directly put the service on scope to use its data or you simply set the properties on an object before you set them on your scope. (Plunkr)
Javascript
app.controller("gnomeList", function(ListService) {
var self = this;
self.list = ListService.value; // value is an object
});
// Create the factory that share the Fact
app.factory('ListService', function($http) {
var list = {};
list.value = {};
list.request = function(theHairColor) {
var theUrl = "https://gnome-shop-fl4m3ph03n1x.c9users.io/api/v1/gnomes?hairColor=" + theHairColor;
console.log(theUrl);
$http({
method: 'GET',
url: theUrl,
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
}).then(function successCallback(response) {
list.value.data = response.data.entries; // extend value object
}, function errorCallback(response) {
console.log('Error: ' + response);
});
};
return list;
});
HTML
<div ng-controller="gnomeList as listCtrl">
<p ng-repeat="gnome in listCtrl.list.data">{{ gnome.id }}: {{ gnome.name }}</p>
</div>
Moreover it is better to use built in angular.extend for extending objects.
So this doesn't work?
list.request = function(theHairColor) {
var theUrl = "https://gnome-shop-fl4m3ph03n1x.c9users.io/api/v1/gnomes?hairColor=" + theHairColor;
console.log(theUrl);
$http({
method: 'GET',
url: theUrl,
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
}).then(function success(data) {
list.data = data;
console.log(data);
return data;
});
};
That would be a structure we've used in the past (we use Restangular now), but this link is a good page to see about $http that would help you.
Been scratching my head for weeks on this particular part of my code. What I have is a simple custom service that would fetch my data via ajax, I then perform a post process and lastly I want to return that data back to my controller. Even tough my service is creating my object correctly nothing ever gets back to my controller.
Ive tried using a "deferred.promise" method, which does seems to return data back to the controller but in a $$state object that I cannot access via the controller for some strange reason. This is a whole other issue.
I just need a simpleway to return data to my controller but all of the post process must be handled in my service. Would anyone know if this is possible?
Here is what i got.
My service:
var app = angular.module('myApp', []);
app.factory('MyService', function($http, $q, $timeout){
var My_Stored_Data = {}
return {
load_my_data_service: function(){
$http({
method: 'GET',
url: 'AJAX PATH',
headers: { "Accept": "application/json;odata=verbose;charset=utf-8"}
}).then(function(data){
//post-process my data before returning it to the controller $scope.return_my_data_here
My_Stored_Data = data;
My_Stored_Data.data_result_1 = My_Stored_Data.data_result_1.split("\r\n");
My_Stored_Data.data_result_2 = My_Stored_Data.data_result_2.split("\r\n");
console.log(My_Stored_Data)//logs shows the that "My_Stored_Data" has processed my data correctly.
return My_Stored_Data //Return my data tot he controller $scope.return_my_data_here.
});
}
}
});
My controller:
app.controller('MyController', function ($scope,$q,MyService) {
$scope.fetch_my_data = function(){
$scope.return_my_data_here = MyService.load_my_data_service();
}
$scope.fetch_my_data() //Initiate on page load
});
#Ali Baig is correct. You need the return as he has pointed out. But the object returned is a promise and not the raw data as you are expecting so you also need this:
app.controller('MyController', function ($scope,$q,MyService) {
$scope.fetch_my_data = function(){
// MyService.load_my_data_service() will return a promise.
$scope.return_my_data_here = MyService.load_my_data_service();
// Use .then() to do something with that promise.
// NOTE: This code will run asynchronously.
$scope.return_my_data_here.then(function(data){
$scope.actualData = data;
console.log($scope.actualData);
});
}
$scope.fetch_my_data() //Initiate on page load
});
I believe you are missing a return statement just before $http({..
return {
load_my_data_service: function(){
return $http({
method: 'GET',
url: 'AJAX PATH',
headers: { "Accept": "application/json;odata=verbose;charset=utf-8"}
}).then(function(result){
return result.data; //to make it cleaner, just return the promise itself to your controller..
});
}
}
This will resolve your service problems. And then in your controller, use
$scope.return_my_data_here = {};
MyService.load_my_data_service().then(function(data){
var My_Stored_Data = data;
My_Stored_Data.data_result_1 = My_Stored_Data.data_result_1.split("\r\n");
My_Stored_Data.data_result_2 = My_Stored_Data.data_result_2.split("\r\n");
$scope.return_my_data_here = My_Stored_Data;
});
I'm trying to set a variable as the data object returned from a http request in angular, but the variable never sets to even if it is in the $scope unless it is nested within the success function. For example, if I do this in the controller :
$scope.hello = [];
var getAppointmentsurl = './dbscripts/getAppointments.php';
$http({method: 'GET', url: getAppointmentsurl}).success(function(data) {
$scope.hello = data;
});
console.log($scope.hello);
}
Hello is blank... so I set it up in services.js like this :
this.getCalendarData=function(){
var hello = [];
var getAppointmentsurl = './dbscripts/getAppointments.php';
$http({method: 'GET', url: getAppointmentsurl}).success(function(data) {
hello = data;
});
return hello;
}
but still hello is blank. Am I missing something obvious?
edit --
this.getCalendarData=function(){
var getAppointmentsurl = './dbscripts/getAppointments.php';
return $http({method: 'GET', url: getAppointmentsurl}).success(function(data) {
return data;
});
}
This is asynchronus call we have to return data like above.
To elaborate on Akash's correct answer, here's an example of how it should work.
In your view you should add logic to show the data only when hello exists. i.e. ng-if="hello"
controller:
ServiceName.getCalendarData().then(function(response) {
$scope.hello = response;
});
service:
this.getCalendarData = function() {
return $http.get('path/to/response/').success(function(data) {
return data;
});
}
As you put the api call as a method in the service, returning data from the service wont resolve yet, So in the controller the service return will only be a promise
serviceName.getCalendarData().then(function(data){
//Success data
},function(){});
Service code must return like the below code and here you will get the entire response object,
return $http({method: 'GET', url:getAppointmentsurl});
One other way to get the data directly resolved stripping of the other properties is returning from service like this,
return $http({method: 'GET', url:getAppointmentsurl}).success(function(data){
return data;
});
I am stuck on some problems , actually I was in problem solved , the problem was header which is not enableing to get response (like CORS issue) ,overcome by using header and transformRequest as shown in below code. After that I got webservice data but in one controller used $rootscope which render some of id of data of second method (API) to use in another controller to put on third one api to get data and I am getting this for only a minute then it will throw error : Cannot read property 'companies data' of null which is field in third api. when I used $rootScope.Test.companies[0].companyname which is store data, and unique for all api like primary key.
var request = $http({
method: "post",
url: "http://app.xyz/xyzapp/public/user/login",
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
transformRequest: function(obj) {
var str = [];
str.push(encodeURIComponent('email_id') + "=" + encodeURIComponent('demo#xyz.com'));
str.push(encodeURIComponent('password') + "=" + encodeURIComponent('demo#xyz'));
return str.join("&");
},
});
request.success(function( response ) {
console.log("Hiiiii::::"+JSON.stringify(response,status));
if (response.status=="success"){
$rootScope.Test1=response.user_id;
var request1 = $http({
method: "post",
url: "http://app.xyz/xyzapp/public/company/getUserCompanyList",
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
transformRequest: function(obj) {
var str = [];
str.push(encodeURIComponent('user_id') + "=" + encodeURIComponent(response.user_id ));
// str.push(encodeURIComponent('password') + "=" + encodeURIComponent('demo#123'));
return str.join("&");
}
});
// getCompany
request1.success(function( response ) {
console.log("Hiiiii::::"+JSON.stringify(response,status)+" "+response.companies.length+":Length");
if (response.status=="success"){
// alert(1);
$state.go('tabdash');
$rootScope.Test = response;
}
});
So please tell me how to use one controller data to another where I am using another api which will get $rootscope date of parent.
Please let me know if anybody know about that or anything
Thanks
Yes you can use variables of one controller inside another controller using two methods
Create Service to communicate between them.
Use $rootScope.$broadcast
sample code
angular.module('myservice', []).service('msgBus', function() {
this.serviceValue= {};
}]);
});
and use it in controller like this:
controller 1
angular.module('myservice', []).controller('ctrl1',function($scope, msgBus) {
$scope.sendmsg = function() {
msgBus.serviceValue='Hello';
}
});
controller 2
angular.module('myservice', []).controller('ctrl2',function($scope, msgBus) {
$scope.checkValue(){
alert( msgBus.serviceValue);
}
});
Below I've got a function that should make a post to the server.
var updateApplicationMetadata = function(appId, editorVersion, previewPubFile){
var deferred = $q.defer();
var result = $http({
method: 'post',
url: '../resource/applications/'+appId,
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
data: {
editorVersion: editorVersion,
previewPubFile: previewPubFile
}
});
result.then(function(data){
deferred.result(data);
console.log('from services: ');
console.log(data);
});
return deferred.promise;
};
I call this function like:
$scope.update = function(){
MessageBus.emitMsg('notification','Application updates successful.');
console.log('from update: ');
console.log($scope.application.appId);
console.log($scope.application.editorVersion);
console.log($scope.application.previewPubFile);
DataContext.updateApplicationMetaData($scope.application.appId,$scope.application.editorVersion ,$scope.application.previewPubFile);
};
All of the values sent to the updateApplicationMetaData are valid. I can use the rest client POST man and I can make the post work. When I do it in angular, however, I get a bad request. The URL and header content type are right, but I'm not sure about the data object. It must be malformed. What am I missing here?
You've got an error in your code. You have:
deferred.result(data);
and it should be:
deferred.resolve(data);
for it to work. Also, you need to pass 'application/json' as your accepts type for your data to work.
Assuming you are using the $q service from https://docs.angularjs.org/api/ng/service/$q I don't see a result method. Perhaps change:
deferred.result(data);
To
deferred.resolve(data);
consider using ngResource for rest style json requests
Angular resource docs