Can't get result data with $http angularjs - http

I'm trying to use $http, but why it return null result?
angular.module('myApp')
.factory('sender', function($http) {
var newData = null;
$http.get('test.html')
.success(function(data) {
newData = data;
console.log(newData)
})
.error(function() {
newData = 'error';
});
console.log(newData)
return newData
})
Console say: http://screencast.com/t/vBGkl2sThBd4. Why my newData first is null and then is defined? How to do it correctly?

As YardenST said, $http is asynchronous so you need to make sure that all functions or display logic that are dependent on the data that is returned by your $http.get(), gets handle accordingly. One way to accomplish this is to make use of the "promise" that $http returns:
Plunkr Demo
var myApp = angular.module('myApp', []);
myApp.factory('AvengersService', function ($http) {
var AvengersService = {
getCast: function () {
// $http returns a 'promise'
return $http.get("avengers.json").then(function (response) {
return response.data;
});
}
};
return AvengersService;
});
myApp.controller('AvengersCtrl', function($scope, $http, $log, AvengersService) {
// Assign service to scope if you'd like to be able call it from your view also
$scope.avengers = AvengersService;
// Call the async method and then do stuff with what is returned inside the function
AvengersService.getCast().then(function (asyncCastData) {
$scope.avengers.cast = asyncCastData;
});
// We can also use $watch to keep an eye out for when $scope.avengers.cast gets populated
$scope.$watch('avengers.cast', function (cast) {
// When $scope.avengers.cast has data, then run these functions
if (angular.isDefined(cast)) {
$log.info("$scope.avengers.cast has data");
}
});
});

This JavaScript code is asynchronous.
console.log(newData)
return newData
Is executed before what inside success
newData = data;
console.log(newData)
So at first time, the newData is null (you set it to be null)
And when the http response is returned (inside the success), the newData gets its new value.
This is very common in Javascript, you should do all your work inside the success.

Related

Unexpected result when using Sinon onCall method

I am experiencing an unexpected result :"should get data and has multiple recordSets" test case. My intention is to have the "stub.getRefundItems" method call return one set of results on the first call and a second set of results on the second call. The test works as expected if there are no other tests that use that method. However when I add in the additional test "should get data and has 1 recordSet" I get a single result (LineItemsHasNextFalse) no matter what I do. I should also mention the stub gets injected via awilix as a singleton. Am I wiring the stub up incorrectly? Is it not possible to change the results using onCall after you have set the returns value?
let stub = {
getRefund: sandbox.stub(),
getRefunds: sandbox.stub(),
getRefundItems: sandbox.stub(),
getRefundTransactions: sandbox.stub()
};
var sandbox = require('sinon').createSandbox();
describe('#refunds', function() {
beforeEach(function () {
sandbox.spy(logger)
})
afterEach(function () {
// completely restore all fakes created through the sandbox
sandbox.restore();
})
context('getRefundItems', function() {
it('should get data and has 1 recordSet', test(async function() {
stub.getRefundItems.returns(LineItemsHasNextFalse)
let resp = await refunds.getRefundItems({"gid":"shjdjs"})
expect(resp).to.deep.equal(LineItemsHasNextFalse.data.refund.refundLineItems.edges)
}))
it('should get data and has multiple recordSets', test(async function() {
let correctResp = new Array()
correctResp.push(...LineItemsHasNextTrue.data.refund.refundLineItems.edges)
correctResp.push(...LineItemsHasNextFalse.data.refund.refundLineItems.edges)
stub.getRefundItems.onCall(0).returns(LineItemsHasNextTrue)
stub.getRefundItems.onCall(1).returns(LineItemsHasNextFalse)
let resp = await refunds.getRefundItems({"gid":"shjdjs"})
console.log(resp)
expect(resp).to.deep.equal(correctResp)
}))
})
})
async getRefundItems(msgObj)
{
let hasNext = false
let items = []
let count = 0
do
{
let response = await stub.getRefundItems(msgObj)
if(response.data.refund != null)
{
items.push(...response.data.refund.refundLineItems.edges)
if(response.data.refund.refundLineItems.pageInfo.hasNextPage)
{
let cursor = response.data.refund.refundLineItems.edges[0].cursor
msgObj.after = cursor
hasNext = true
}
else
{
hasNext=false
}
}
}
while(hasNext)
return items
}

Meteor wrapAsync returning undefined to external service using php connector

I've been stuck on this for way too long. Can anyone help show me why my wrapasync method is returning as "undefined"? I'm trying to make a call to Facebook api and have used a php connector that my meteor app should use to talk to the Facebook PHP SDK. My app talks to the my php file and returns simple functions. But when I actually call the external service it comes back as undefined, although when I run the php function in php it comes out fine and returns expected results. Here's the code, everything is server-side:
var phpAsync = function() {
var execPhp = Meteor.npmRequire('exec-php');
execPhp('C:/xampp/htdocs/xampp/Facebook/file.php', function(error, php, output){
// php now contain user defined php functions.
php.me( function(error, result, output, printed){
// `result` is return value of `my_own_php_function` php function.
console.log(result);
return result;
});
});
};
Meteor.methods({
phpConnect: function () {
var phpSync = Meteor.wrapAsync(phpAsync);
var result = phpSync();
console.log(result);
return result
}
});
The function that is passed in to Meteor.wrapAsync should have callback as the final parameter. From the docs,
Wrap a function that takes a callback function as its final parameter.
The signature of the callback of the wrapped function should be
function(error, result){}
So you should do something like this,
var phpSync = function() {
var execPhp = Meteor.npmRequire('exec-php');
var execPhpSync = Meteor.wrapAsync(execPhp);
var php = execPhpSync('C:/xampp/htdocs/xampp/Facebook/file.php');
php.meSync = Meteor.wrapAsync(php.me);
var result = php.meSync();
console.log(result);
return result;
};
Meteor.methods({
phpConnect: function () {
var result = phpSync();
console.log(result);
return result;
}
});
OR
if you want other stuff like output and printed then you do it like this
var phpAsync = function(cb) {
var execPhp = Meteor.npmRequire('exec-php');
execPhp('C:/xampp/htdocs/xampp/Facebook/file.php', function(error, php, output){
//IMPORTANT: you need to call php.me only if there is no error
// php now contain user defined php functions.
php.me( function(error, result, output, printed){
// `result` is return value of `my_own_php_function` php function.
if (typeof cb === "function) {
cb(error, {
result: result,
output: output,
printed: printed
});
}
});
});
};
Meteor.methods({
phpConnect: function () {
var phpSync = Meteor.wrapAsync(phpAsync);
var result = phpSync();
console.log(result.result, result.output, result.printed);
return result.result;
}
});

Method call that fetches data. Can it be made reactive?

I want to get data from a number of queries on the same collection, and unfortunately this is not yet supported on meteor. That's why I tried to do something like this:
Common
Dep = new Deps.Dependency;
Server
Meteor.methods({
fetch: function(){
var results = Data.find(dataQuery).fetch();
var otherResults = Data.find(queryThatCannotBeCombinedWithPrevious).fetch();
return results.concat(otherResults);
},
save: function(data){
Data.insert(data);
Dep.changed();
}
update: function(data){
Data.update({_id: data._id}, data);
Dep.changed();
}
});
Client
Session.setDefault('combinedData', []);
Template.demo.data = function(){
Dep.depend();
Meteor.call('fetch',function(error, data){
Session.set('combinedData', data);
});
return Session.get('combinedData');
};
This doesn't work though, propably because the Dep variable on the client is different from the Dep on the server. Is there a way to make the method call reactive when the contents of the Data collection change?
Notes
I am currently using Meteor 0.8.1.1, which doesn't allow subscriptions that return multiple cursors of the same collection yet.
This requires a small hack and you're close. First, you need a client–only dependence, the server just passes a data returned from the method and doesn't share variables (also there's nothing on the server that requires deps). Second, you only want to fetch the actual variable once, otherwise you'll end up with an infinite loop.
Example implementation:
var value = null;
var valueInitialized = false;
var valueDep = new Deps.Dependency();
Template.demo.data = function() {
valueDep.depend();
if(!valueInitialized) {
valueInitialized = true;
Meteor.call('fetchData', function(err, result) {
value = result;
valueDep.changed();
});
}
return value;
};

Block meteor helper from returning when doing http request

I need to prevent my meteor helper from returning right away until either the timeout or data returns from my Meteor.http.get(url, ...) request. For example,
Meteor.templateName.helpers ({
testHelper: function()
{
var ss = "doesnt wait";
Meteor.http.get("http://api.somesite.com",
function (error, result) {
if(!error){
if(result.statusCode === 200) {
var respJson = JSON.parse(result.content);
console.log(respJson);
ss = "should have this value";
}
}
});
return ss;
}
})
Is Meteor.http not a blocking call, how can i make the helper method stop until the get request returns data. Do i need to move the get request to a Meteor.Method ?
On the client, you don't have the fiber module, as a result is not possible to make a synchronous call to a function.
One solution might be to use a Session because of it's reactivity. You just set a default value, and use it in your helper function
Session.setDefault('testHelper', {msg: 'wait'})
Meteor.templateName.helpers ({
testHelper: function() {
return Session.get('testHelper');
}
});
Then update this session every time you want:
Template.templateName.rendered = function () {
Meteor.http.get("http://api.somesite.com",
function (error, result) {
if(!error && result.statusCode === 200){
var respJson = JSON.parse(result.content);
Session.set('testHelper', respJson)
}
}
);
}
If you don't want to use a Session, you can implement your own reactivity mecanism using the Deps module. For instance in the Meteor.http.get callback you can set a Template.templateName attribute and invalidate a context object in order to rerun the helper function. But a Session is definitly more easy ;-)

How to $http Synchronous call with AngularJS

Is there any way to make a synchronous call with AngularJS?
The AngularJS documentation is not very explicit or extensive for figuring out some basic stuff.
ON A SERVICE:
myService.getByID = function (id) {
var retval = null;
$http({
url: "/CO/api/products/" + id,
method: "GET"
}).success(function (data, status, headers, config) {
retval = data.Data;
});
return retval;
}
Not currently. If you look at the source code (from this point in time Oct 2012), you'll see that the call to XHR open is actually hard-coded to be asynchronous (the third parameter is true):
xhr.open(method, url, true);
You'd need to write your own service that did synchronous calls. Generally that's not something you'll usually want to do because of the nature of JavaScript execution you'll end up blocking everything else.
... but.. if blocking everything else is actually desired, maybe you should look into promises and the $q service. It allows you to wait until a set of asynchronous actions are done, and then execute something once they're all complete. I don't know what your use case is, but that might be worth a look.
Outside of that, if you're going to roll your own, more information about how to make synchronous and asynchronous ajax calls can be found here.
I hope that is helpful.
I have worked with a factory integrated with google maps autocomplete and promises made​​, I hope you serve.
http://jsfiddle.net/the_pianist2/vL9nkfe3/1/
you only need to replace the autocompleteService by this request with $ http incuida being before the factory.
app.factory('Autocomplete', function($q, $http) {
and $ http request with
var deferred = $q.defer();
$http.get('urlExample').
success(function(data, status, headers, config) {
deferred.resolve(data);
}).
error(function(data, status, headers, config) {
deferred.reject(status);
});
return deferred.promise;
<div ng-app="myApp">
<div ng-controller="myController">
<input type="text" ng-model="search"></input>
<div class="bs-example">
<table class="table" >
<thead>
<tr>
<th>#</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="direction in directions">
<td>{{$index}}</td>
<td>{{direction.description}}</td>
</tr>
</tbody>
</table>
</div>
'use strict';
var app = angular.module('myApp', []);
app.factory('Autocomplete', function($q) {
var get = function(search) {
var deferred = $q.defer();
var autocompleteService = new google.maps.places.AutocompleteService();
autocompleteService.getPlacePredictions({
input: search,
types: ['geocode'],
componentRestrictions: {
country: 'ES'
}
}, function(predictions, status) {
if (status == google.maps.places.PlacesServiceStatus.OK) {
deferred.resolve(predictions);
} else {
deferred.reject(status);
}
});
return deferred.promise;
};
return {
get: get
};
});
app.controller('myController', function($scope, Autocomplete) {
$scope.$watch('search', function(newValue, oldValue) {
var promesa = Autocomplete.get(newValue);
promesa.then(function(value) {
$scope.directions = value;
}, function(reason) {
$scope.error = reason;
});
});
});
the question itself is to be made on:
deferred.resolve(varResult);
when you have done well and the request:
deferred.reject(error);
when there is an error, and then:
return deferred.promise;
var EmployeeController = ["$scope", "EmployeeService",
function ($scope, EmployeeService) {
$scope.Employee = {};
$scope.Save = function (Employee) {
if ($scope.EmployeeForm.$valid) {
EmployeeService
.Save(Employee)
.then(function (response) {
if (response.HasError) {
$scope.HasError = response.HasError;
$scope.ErrorMessage = response.ResponseMessage;
} else {
}
})
.catch(function (response) {
});
}
}
}]
var EmployeeService = ["$http", "$q",
function ($http, $q) {
var self = this;
self.Save = function (employee) {
var deferred = $q.defer();
$http
.post("/api/EmployeeApi/Create", angular.toJson(employee))
.success(function (response, status, headers, config) {
deferred.resolve(response, status, headers, config);
})
.error(function (response, status, headers, config) {
deferred.reject(response, status, headers, config);
});
return deferred.promise;
};
I recently ran into a situation where I wanted to make to $http calls triggered by a page reload. The solution I went with:
Encapsulate the two calls into functions
Pass the second $http call as a callback into the second function
Call the second function in apon .success
Here's a way you can do it asynchronously and manage things like you would normally.
Everything is still shared. You get a reference to the object that you want updated. Whenever you update that in your service, it gets updated globally without having to watch or return a promise.
This is really nice because you can update the underlying object from within the service without ever having to rebind. Using Angular the way it's meant to be used.
I think it's probably a bad idea to make $http.get/post synchronous. You'll get a noticeable delay in the script.
app.factory('AssessmentSettingsService', ['$http', function($http) {
//assessment is what I want to keep updating
var settings = { assessment: null };
return {
getSettings: function () {
//return settings so I can keep updating assessment and the
//reference to settings will stay in tact
return settings;
},
updateAssessment: function () {
$http.get('/assessment/api/get/' + scan.assessmentId).success(function(response) {
//I don't have to return a thing. I just set the object.
settings.assessment = response;
});
}
};
}]);
...
controller: ['$scope', '$http', 'AssessmentSettingsService', function ($scope, as) {
$scope.settings = as.getSettings();
//Look. I can even update after I've already grabbed the object
as.updateAssessment();
And somewhere in a view:
<h1>{{settings.assessment.title}}</h1>
Since sync XHR is being deprecated, it's best not to rely on that. If you need to do a sync POST request, you can use the following helpers inside of a service to simulate a form post.
It works by creating a form with hidden inputs which is posted to the specified URL.
//Helper to create a hidden input
function createInput(name, value) {
return angular
.element('<input/>')
.attr('type', 'hidden')
.attr('name', name)
.val(value);
}
//Post data
function post(url, data, params) {
//Ensure data and params are an object
data = data || {};
params = params || {};
//Serialize params
const serialized = $httpParamSerializer(params);
const query = serialized ? `?${serialized}` : '';
//Create form
const $form = angular
.element('<form/>')
.attr('action', `${url}${query}`)
.attr('enctype', 'application/x-www-form-urlencoded')
.attr('method', 'post');
//Create hidden input data
for (const key in data) {
if (data.hasOwnProperty(key)) {
const value = data[key];
if (Array.isArray(value)) {
for (const val of value) {
const $input = createInput(`${key}[]`, val);
$form.append($input);
}
}
else {
const $input = createInput(key, value);
$form.append($input);
}
}
}
//Append form to body and submit
angular.element(document).find('body').append($form);
$form[0].submit();
$form.remove();
}
Modify as required for your needs.
What about wrapping your call in a Promise.all() method i.e.
Promise.all([$http.get(url).then(function(result){....}, function(error){....}])
According to MDN
Promise.all waits for all fulfillments (or the first rejection)

Resources