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.
Related
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;
});
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
});
}
]);
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);
}
});
I trying to get some data and then pass it to the controller for further processing. I have setup a factory to get the data but having issues passing it to the controller and getting it working. Below is the case I'm working with.
var app = angular.module("contactApp",[]);
app.factory('listData',function($http){
return{
getData: function(onSuccess,onFailure,itemID){
$http.get("/_api/web/lists/getbytitle('Consultant%20Profile')/items?$filter=ID%20eq%20"+itemID).success(onSuccess).error(onFailure);
}
};
});
app.controller('ContactController', function(listData,$scope){
//setTimeout(function(){console.log(Data)},2000);
console.log("Controller called. Hello");
listData.getData(successFunction,failFunction,curItemId);
successFunction = function(data){
$scope.resData = data;
console.log("Success - ", data);
}
failFunction - function(data){
console.log("Didn't work - ", data);
}
});
This gives me below error.
successFunction is not defined
Not sure what I'm doing wrong, any input will be greatly appreciated!
EDIT:
Moving the functions down works really well but the async call is failing. I switched to using $ajax and it works just fine but $http doesn't work for some reason!
app.factory('listData',function($http){
return{
getData: function(onSuccess,onFailure,itemID){
//$http.get("/_api/web/lists/getbytitle('Consultant%20Profile')/items?$filter=ID%20eq%20156").success(onSuccess).error(onFailure);
$.ajax({
url:"/_api/web/lists/getbytitle('Consultant%20Profile')/items?$filter=ID%20eq%20"+itemID,
headers: {"accept": "application/json; odata=verbose"},
success: onSuccess,
error: onFailure
});
}
};
});
Just fyi the data is coming from a SharePoint list but that shouldn't matter. I'll keep digging and please do let me know if I'm making any syntax error that I can't locate.
I really appreciate the help guys!
EDIT 2:
Ok this issue was unrelated. I found the problem, SharePoint uses odata so I had to pass a header:
app.factory('listData',function($http){
return{
getData: function(onSuccess,onFailure,itemID){
$http(
{
method: "GET",
headers: {"accept": "application/json; odata=verbose"},
url: "/_api/web/lists/getbytitle('Consultant%20Profile')/items?$filter=ID%20eq%20"+itemID
}
).success(onSuccess).error(onFailure);
}
};
});
You guys ROCK!!!!!!!
As Daniel A. White said, declare your functions before you call them.
var app = angular.module("contactApp",[]);
app.factory('listData',function($http){
return{
getData: function(onSuccess,onFailure,itemID){
$http.get("/_api/web/lists/getbytitle('Consultant%20Profile')/items?$filter=ID%20eq%20"+itemID).success(onSuccess).error(onFailure);
}
};
});
app.controller('ContactController', function(listData,$scope){
//setTimeout(function(){console.log(Data)},2000);
console.log("Controller called. Hello");
var successFunction = function(data){
$scope.resData = data;
console.log("Success - ", data);
}
var failFunction - function(data){
console.log("Didn't work - ", data);
}
//Now your functions are actually defined.
listData.getData(successFunction,failFunction,curItemId);
});
You don't have to use var to declare functions, because JavaScript will still understand but using var makes it a local variable, while not using var will define it as global.
I'm sure this will help.
I have a :
ng-click="like(photo.Id)"
...which fires a http post call to update data in the database.
The photo object comes from a ViewModel and can be accessed in the HTML by using ex {{photo.Likes}}. This.. gives me the number of likes and works like it should..
But.. I need to update the count in the html page when the ng-click="like...." function is clicked by the user.
So.. I am trying to do something like this :
<span ng-watch="{{photo.Likes}}"></span>
but clearly.. I don't understand well enough how this stuff works.
Here is the $http call which fills $scope.photo. Do i need to add some watch logic here too ?
$http({
method: 'GET',
url: '/api/ViewImage/GetPhotoById/' + $routeParams.id,
accept: 'application/json'
})
.success(function (result) {
console.log(result);
$scope.photo = result;
});
Do you mean:
<span>{{photo.Likes}}</span>
Just putting expression in the double mustaches like {{ photo.Likes }} makes a binding which will update your html once the value is updated.
Create a method for ng-click that will change the value (I think you got this part already)
So overall it going to be something like below:
<span ng-click='like(photo.photoId)'>like</span>
<span>{{photo.Likes}}</span>
And in controller:
....controller(function($scope, $http) {
$scope.photo = null
$scope.like = function(photo) {
photo.Likes++
}
$http({
method: 'GET',
url: '/api/ViewImage/GetPhotoById/' + $routeParams.id,
accept: 'application/json'
})
.success(function (result) {
console.log(result);
$scope.photo = result;
});
})
ng-watch as a directive, does not exist.
You need to create something within your controller, service, directive etc to use: $scope.$watch