How to update $scope variables through a function - javascript

I'm currently working on grabbing some data from an endpoint and then updating a variable called $scope.response. I'm not quite sure how to update this variable and render it on screen.
So what happens in the code below:
I get the query string from an iframe's src attribute, and then post it to my endpoint, where I get a particular response called data. I'd like to update $scope.response with this object, and then render it on the view using {{response}}.
Could someone show me how I could do this?
app.controller('MainCtrl', function($scope) {
$scope.response;
API = {};
API.endpoint = 'https://some/endpoint/';
function getParameterByName(name, url) {
if (!url) url = window.location.href;
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
function doAjax(callback) {
var q = getParameterByName('QUERY');
jQuery.ajax({
url: API.endpoint + "script.php",
method: "POST",
data: { q: q },
dataType: "json",
success: function (data) {
callback(data);
$scope.response = data;
}
});
}
});

Angular doesn't magically know when a property on an object changes, it would have to keep re-checking all objects all the time to catch such changes. Angular just makes it look like it notices such changes whenever you use any Angular services or events, since those trigger a digest cycle. At the end of a digest cycle, Angular checks objects it knows about for changes and propagates those changes (e.g. updates views etc.).
When you use jQuery, that's "outside" of what Angular knows about. Primarily you should not use jQuery, but Angular's $http service to make any network requests, since Angular will then properly cycle its digestive system.*
* Pun totally intended
If you have to use some non-Angular system (and again, you really don't have to here, at all), then you need to trigger another digest cycle. The best way to do that is with the $timeout service:
app.controller('MainCtrl', function($scope, $timeout) {
...
success: function (data) {
callback(data);
$timeout(() => $scope.response = data);
}
...
});

Why do you use jQuery in Angular?. If you choose Angular, you should be you $http in angular. Remove function doAjax and replace it to $http. You can read doc in here https://docs.angularjs.org/api/ng/service/$http

Related

Sometimes data does not populate from a database [duplicate]

This question already has answers here:
AngularJS : returning data from service to controller
(2 answers)
Why are Callbacks from Promise `.then` Methods an Anti-Pattern
(2 answers)
Why are AngularJS $http success/error methods deprecated? Removed from v1.6?
(2 answers)
Closed 4 years ago.
About 1 in every 10 times I refresh my project, the data from my database is not retrieved and I have to refresh the page again to get it. I tried adding a 101ms timeout onto the function which retrieves the data, and this worked- but also removed a lot of other functionality of the site.
The problem is that sometimes the page loads before the data is retrieved.
Is there a better way to solve this than using a timeout? My code for the POST is below:
get_data: function (kpi, date_from, date_to, callback) {
var config = {
method: 'POST',
url: '/getData',
data: {
kpi: kpi,
date_from: date_from,
date_to: date_to
}
};
$http(config)
.success(function (data) {
callback(data);
})
.error(function (data, status, headers, config) {
console.log(data, status, headers, config);
});
}
And then where this method is called (currently with the timeout, which is breaking other site functionality):
$scope.update_all_data = $timeout(function(){
$scope.show_loading = true;
var date_from = findDateUnix($scope.myDateFrom, $scope.availableDates);
var date_to = findDateUnix($scope.myDateTo, $scope.availableDates);
UpdateSvc.get_data($scope.kpi_selected, date_from, date_to, function(res){
raw_data = [];
if(res.raw_data != null) {
if(res.raw_data.length > 0){
raw_data = res.raw_data;
skey_data = [];
if(res.skey_data != null) {
skey_data = res.skey_data;
}
var num = $scope.show_tab.indexOf(true);
$scope.filtering = generateCategories();
$timeout(function(){
$scope.display_nodata = false;
resizeObjects();
$('.md-datepicker-input').prop('disabled', true);
//Take out loading
$scope.show_loading = false;
}, 100);
}else{
$scope.display_nodata = true;
$timeout(function(){ $scope.show_loading = false;}, 100);
}
}else{
$scope.display_nodata = true;
$timeout(function(){ $scope.show_loading = false;}, 100);
}
});
}, 101);
The problem is that sometimes the page loads before the data is retrieved.
I'm not sure what you mean by this. In an AngularJS application, the entire page must be loaded before AngularJS even begins to work. So your data (retreived with $http) will ALWAYS be loaded after the page loads, unless it is injected server-side.
I don't think you are having any issues retrieving the data from $http. You can easily confirm this by eliminating or commenting out all the code that does not deal directly with data retrieval. I would also strongly recommend the following changes:
First, using callbacks with $http is an antipattern: Why are Callbacks from Promise `.then` Methods an Anti-Pattern
(Note: newer versions of AngularJS use then/catch instead of success/error)
You should, instead use the built-in promise system. Calls to $http return a promise object that can be used directly like so:
get_data: function (kpi, date_from, date_to) {
var config = {
method: 'POST',
url: '/getData',
data: {
kpi: kpi,
date_from: date_from,
date_to: date_to
}
};
// return the $http call
return $http(config)
.error(function (data, status, headers, config) {
console.log(data, status, headers, config);
});
}
There is not really any reason to call $timeout in update_all_data function. You are adding 101 seconds AFTER your $http call has already returned. All that does is delay the display of the data further. Here, we are also calling the success function on the promise returned from the $http call in the service:
$scope.update_all_data = function(){
var date_from = findDateUnix($scope.myDateFrom, $scope.availableDates);
var date_to = findDateUnix($scope.myDateTo, $scope.availableDates);
UpdateSvc.get_data($scope.kpi_selected, date_from, date_to).success(function(data) {
console.log(data);
});
});
If this works and your data is logged 10/10 times then the issue is not with data retreival, it is with the rest of the code in your update_all_data function.

Restful call from angular is not returning results

Here am trying to make a RESTful call to an external API.
am trying to achieve 2 things in one call. So, I have one function with 2 nested functions within.
The first one calls the search API to search for a product.
The second one calls recommended API to retrieve recommendations based on the results from the first one.
My AngularJS Code is as follow;
var walmartAssn= angular.module('myApp', ['ngResource']);
walmartAssn.controller('walmartAssnController', function($scope,$resource) {
//define the API urls
var urlSearchProductApi= 'http://api.walmartlabs.com/v1/search';
var urlRecProductApi='http://api.walmartlabs.com/v1/nbp';
//define API key
var keyApi='exampleKey123';
$scope.searchProductMethod= function(){
//pass the value from the user input text box
$scope.searchItem = $scope.item ;
$scope.productId;
//get the data from the Walmart product search API
searchRequest = $resource(urlSearchProductApi, { callback:
"JSON_CALLBACK" }, { get: { method: "JSONP" }});
//pass the input text as a parameter through a GET request
$scope.searchedProducts = searchRequest.get({ apiKey: keyApi,
query: $scope.searchItem });
console.log($scope.searchedProducts.$promise);
$scope.searchedProducts.$promise.then(function(eventDetail){
//fetch the ID of the first item
$scope.productId = eventDetail.items[0].itemId;
});
recommendRequest = $resource(urlRecProductApi, { callback:
"JSON_CALLBACK" }, { get: { method: "JSONP" , isArray:true}});
console.log(recommendRequest);
$scope.recommendedProducts = recommendRequest.get({ apiKey:
keyApi, itemId: 42608121 });
console.log($scope.recommendedProducts)
$scope.recommendedProducts.$promise.then(function(){
$scope.recommendedProductsList = eventDetail;
console.log("Print recommended list");
console.log(eventDetail);
console.log($scope.recommendedProductsList);
console.log('End');
});
} });
In the above app, the first function returns result while the second function does not.
In chrome console am getting the following, not the fist function returns an array of JSONs while the second one was blocked.
While on the Network tab in the chrome console, i see the the call was successful, as in shown below;
Moreover, I have tried the URL with hard coded values in the browser and worked successfully.
Any help is appreciated, thanks in advance.
Assuming that the 2nd call does not depend on the first, I see that you are not defining eventDetail as an argument to the second method.
So, instead of:
$scope.recommendedProducts.$promise.then(function(){
It would be:
$scope.recommendedProducts.$promise.then(function(eventDetail){
If you actually mean to use the eventDetail from the first method (the one used with $scope.searchedProducts.$promise), then the whole second request code needs to be called from the first then handler, passing the data needed.
Something like:
var walmartAssn= angular.module('myApp', ['ngResource']);
walmartAssn.controller('walmartAssnController', function($scope,$resource) {
//define the API urls
var urlSearchProductApi= 'http://api.walmartlabs.com/v1/search';
var urlRecProductApi='http://api.walmartlabs.com/v1/nbp';
//define API key
var keyApi='exampleKey123';
$scope.recommend = function(itemId) {
var recommendRequest = $resource(urlRecProductApi, { callback:
"JSON_CALLBACK" }, { get: { method: "JSONP" , isArray:true}});
console.log(recommendRequest);
$scope.recommendedProducts = recommendRequest.get({ apiKey:
keyApi, itemId: itemId });
console.log($scope.recommendedProducts);
$scope.recommendedProducts.$promise.then(function(eventDetail){
$scope.recommendedProductsList = eventDetail.items; // or just `eventDetail`?
console.log("Print recommended list");
console.log(eventDetail);
console.log($scope.recommendedProductsList);
console.log('End');
});
};
$scope.searchProductMethod= function(){
//pass the value from the user input text box
$scope.searchItem = $scope.item ;
$scope.productId;
//get the data from the Walmart product search API
var searchRequest = $resource(urlSearchProductApi, { callback:
"JSON_CALLBACK" }, { get: { method: "JSONP" }});
//pass the input text as a parameter through a GET request
$scope.searchedProducts = searchRequest.get({ apiKey: keyApi,
query: $scope.searchItem });
console.log($scope.searchedProducts.$promise);
$scope.searchedProducts.$promise.then(function(eventDetail){
//fetch the ID of the first item
$scope.productId = eventDetail.items[0].itemId;
$scope.recommend($scope.productId);
});
};
});
One more thing:
Why is isArray:true used only in recommendation but not in search?
Update
It might be worth trying a jQuery JSONP call to see if it works. Maybe the recommendation endpoint does not support JSONP. AngularJS returns 404 in this case according to https://stackoverflow.com/a/24893912/146656

angularjs XXXService.XXXfunction is not a function

It is my first try writing up a service in AngularJS. I have a service, in which I have a function. When I call this service from my controller, it says XXXService.XXXfunction is not a function.
My Javascript is like,
var sharecycle0_angularfire = angular.module("sharecycle0_angularfire", []);
sharecycle0_angularfire.service("baseHttp", function($http, $q){
self.getFire = function(){
var deferred = $q.defer();
$http({
method: GET,
url: "https://boiling-inferno-1234.firebaseio.com/Cat"
}).then(function(response){
deferred.resolve(response);
},function(response){
deferred.reject(resposne);
});
return deferred.promise;
};
});
sharecycle0_angularfire.controller("angularfirecontroller", function($scope, baseHttp, $window){
$scope.fireClick = function(){
baseHttp.getFire()
.then(
function(response)
{
$("<div/>").text(response).appendTo($("#successData"));
},
function(response)
{
$window.alert("error: "+response);
}
);
};
});
My HTML is like,
<body ng-controller="angularfirecontroller">
<button id="fireClickButton" ng-click="fireClick()">fireClick</button>
<div id="successData"/>
</body>
I expect the service code to run upon my button being hit.
You have some issues here, you are not attaching the function getFire to the service instance, instead you are attaching it to the global variable self. self global variable is generally an alias to window object in many browsers so you are attaching the function to the window and not to the controller instance. Since http already returns a promise you do not need to create a deferred object there, instead just return the result of $http. Apart from that GET needs to be a string value, you are trying to use an undefined variable GET and not "GET". Many a times placing a debug log in relevant areas of the code will help you diagnose the issue.
All you would need is:
sharecycle0_angularfire.service("baseHttp", function($http, $q){
this.getFire = function(){
return $http({
method: 'GET',
url: "https://boiling-inferno-1234.firebaseio.com/Cat"
});
}
});
if you intended to cache this then you should create a local variable and assign this to that.
i.e
sharecycle0_angularfire.service("baseHttp", function($http, $q){
var _this = this; //Or var self = this
_this.getFire = function(){
return $http({
method: 'GET',
url: "https://boiling-inferno-1234.firebaseio.com/Cat"
});
}
});

How do I set a variable within an Angular JS service to the response data from an $http call within an AngularJS service?

I have the following service which I eventually want to cache. However, I can't seem to figure out how set a variable within the service to the response data from the REST call using $http in Angular.
routerApp.service('activityService', function($http) {
delete $http.defaults.headers.common['X-Requested-With'];
this.temp = null;
this.setTemp(x) {
this.temp = x;
}
this.getActivities = function(x) {
var promise = $http({
method: 'GET',
url: 'my url is here...'
}).then(function(response) {
//setTemp(response.data); //definitely wont work
return response.data;
});
//how to set temp to response.data???
setTemp(promise.data); //doesn't work -
};
});
I don't know JS (or angular for that matter) very well. What is the best way to do this?
There is no need to cache the angular service, it's guaranteed to be a singleton.
If you mean to cache the response data, you will create a cache object in your service.
Now about the main question. In this code there is misuse of promises not angular services. Promises are asynchronous, meaning that a callback provided to .then() will be executed some time later, when request is finished. Besides .then returns another promise, which you should return from activityService.getActivities method.
this.getActivities = function(x) {
var promise = $http({
method: 'GET',
url: 'my url is here...'
}).then(function(response) {
setTemp(response.data); //meaning that setTemp somehow modifies it's argument
return response.data;
});
return promise;
};
Then in one of your controllers you will use this service's method attaching .then to it's return value.
.controller('someController', function (activityService) {
activityService.getActivities()
.then(function (data) {
doSomethingWithResponse(data);
}
});

Wrap angular $resource requests not returning POST data

I'm working on wrapping my $resource requests in a simple wrapper. The main idea
is to be able to add some logic before the request is made. I've followed the nice article written by Nils.
Here you can see a service definition to access the REST API module.
resources.factory('Device', ['RequestWrapper', '$resource', 'lelylan.config', function(RequestWrapper, $http, config) {
var resource = $resource(config.endpoint + '/devices/:id', { id: '#id' });
return RequestWrapper.wrap(resource, ['get', 'query', 'save', 'delete']);
}]);
And here you can see the request wrapper definition.
resources.factory('RequestWrapper', ['AccessToken', function(AccessToken) {
var requestWrapper = {};
var token;
requestWrapper.wrap = function(resource, actions) {
token = AccessToken.initialize();
var wrappedResource = resource;
for (var i=0; i < actions.length; i++) { request(wrappedResource, actions[i]); };
return wrappedResource;
};
var request = function(resource, action) {
resource['_' + action] = resource[action];
resource[action] = function(param, data, success, error) {
(AccessToken.get().access_token) ? setAuthorizationHeader() : deleteAuthorizationHeader()
return resource['_' + action](param, data, success, error);
};
};
var setAuthorizationHeader = function() {
$http.defaults.headers.common['Authorization'] = 'Bearer ' + token.access_token;
};
var deleteAuthorizationHeader = function() {
delete $http.defaults.headers.common['Authorization']
};
return requestWrapper;
}]);
Everything works just fine for the GET and DELETE methods (the ones that does not returns
a body seems), but I can't get $save working. What happens is that when the JSON of the
created resources returns it is not added. I have only the data I've set on the creation
phase. Let me make an example.
In this case we use the wrapped resource. If I try to get the #updated_at attribute I can't
see it. In the Chrome inspector I can see how the resource is successfully created.
$scope.device = new Device({ name: 'Angular light', type: 'http://localhost:9000/types/50bf5af4d033a95486000002' });
$scope.device.$save(function(){ console.log('Device Wrapped', $scope.device.created_at) });
# => undefined
If I use $resource everything works fine.
// Suppose authorization is already set
var Resource = $resource('http://localhost\\:9000/devices/:id');
$scope.resource = new Resource({ name: 'Angular light', type: 'http://localhost:9000/types/50bf5af4d033a95486000002' });
$scope.resource.$save(function(){ console.log('Device Base', $scope.resource.created_at); });
# => 2013-02-09T12:26:01Z
I started to check the angular-resource.js code but after few hours I couldn't really figure
it out. I can't get why the body is returned, but in the wrapper resource it is not accessible.
Any idea or help would be appreciated. Thanks.
While diving into AngularJS source code I've found the solution.
The problem was that the wrapper was returning a function instead of an object and this was giving some problems. The solution is to change the following row in the Wrapper:
return resource['_' + action](param, data, success, error);
with this one:
return resource['_' + action].call(this, params, data, success, error);
Why? The fast answer is because in the source code of angular-resource they use it. Actually #call run the function sending this to the calling object. It is often used to initialize an object. Learn more here.

Categories

Resources