I'm currently working on an angular application i'll post some sample code here.
In My Service i have a method that access a rest api via $resource:
function getData() {
resource.get({}, function success(data) {
return data;
}, function error() {
console.log('error')
})
}
Now over in a controller that has been injected with the service i attempt to use the data with some conditions from the previous page that are passed through as a routeParameter as follows:
$scope.data = MyResource.getData()
$scope.editingObject = angular.copy($scope.data[$routeParams.objIndex])
On my view i would like to bind to $scope.editingObject but since that code executes immediately and the data may not have arrived back from the server i receive the following error:
TypeError: Cannot read property '0' of undefined
Now i understand that i cannot access the array before the data has arrived back from the server but my question is more about how do i construct this service in such a way that i can access that data?
To resolve this issue i have attempted to place a watch on $scope.data and monitor for the data to arrive but it never fires when the data does arrive. i have confirmed this using console.log in a set timeout to check after a few seconds if the data has arrived at which point it has... and the object is updated to reflect this.
Could anyone please point me in the right direction regarding services, resources and accessing the data?
I would like to see the $watch() code because it should fire when data is received. But anyways, you should use the promise returned from service:
MyResource.getData().then(function(data) {
$scope.data = data;
$scope.editingObject = angular.copy($scope.data[$routeParams.objIndex])
});
Related
I'm working on a Meteor project and want to get the return value of Meteor.call in template helpers on client side. At very first, I just set a variable in the call back function and get the variable's value outside the Meteor.call. I found out the code after Meteor.call doesn't execute at all. Then I searched a bit and use Session, it works. But I don't really understand the reason. Here's my original code and modified code. Can anyone explain a bit for me? Thanks!!
Original wrong code: html
<div id="text-result-main">
<h2>{{title}}</h2>
</div>
js
Template.texts.helpers({
title: function(){
var index = Router.current().params.index;
Meteor.call('getTitle', index,function(error, result){
titles = result;
});
console.log(titles);
return titles;
}});
Collection text.js
Text = new Mongo.Collection("text");
Meteor.methods({
'getTitle': function(myindex){
return Text.findOne({index: myindex}).title;
}});
The working code: js
Template.texts.helpers({
title: function(){
var index = Router.current().params.index;
Meteor.call('getTitle', index,function(error, result){
Session.set("titles",result);
});
console.log(Session.get("titles"));
return Session.get("titles");
}});
Notice that I didn't publish Collection Text to the client at all because it's just so huge. Every time when I refresh the page when running the wrong code, I can't see the content of "title" or see it on the console. But when I set the session, it works. I don't really understand how it works here. Thanks
There is two issues Asynchronicity and Reactivity
This affectation
Meteor.call('getTitle', index,function(error, result){
titles = result;
});
inside the meteor call is executed but in a asynch way. So the return of your helper is immediately called, and return a empty value.
Try it out in the console of your browser.
But then, why your template render correctly with {{title}} when you use a Session Variable ?
It's because the Session is a reactive data source, witch means that every change to it trigger a re-computation of all templates involving this piece of data.
Here is a timeline:
Methods is called
Return empty value
Method is executed, setting variable value
If the Variable is a reactive data source, template is re-computed. ( in your case, the session is a reactive data source. )
To go further
I would use a reactive var in that case, it's very close from a session variable, but the scope is limited to a template.
A good read on Reactive data source: http://richsilv.github.io/meteor/meteor-reactive-data-types/
The problem is the fact that Meteor.call() is asynchronous when paired with a callback.
So when title() starts executing, it does not wait for your Meteor.call() invocation to return a result (or possibly an error). It continues execution. This is called asynchronous execution.
In short, you are trying to log the value for the key titles which doesn't exist in Session (since the state of your asynchronous Meteor call is unknown, at this point of time).
Try moving the console log statement into the callback paired with your Meteor.call() and you can see the result once it has successfully been set in Session.
A workaround to your problem is to make your Meteor.call() synchronous like this:
Template.texts.helpers({
title: function(){
var index = Router.current().params.index;
var result = Meteor.call('getTitle', index); // <--- this is synchronous code now
Session.set("titles",result);
console.log(Session.get("titles"));
return Session.get("titles");
}});
Removing the callback makes Meteor.call() behave synchronously.
If you do not pass a callback on the server, the method invocation
will block until the method is complete. It will eventually return the
return value of the method, or it will throw an exception if the
method threw an exception.
(from http://docs.meteor.com/api/methods.html#Meteor-call)
Why not use something like this:
title: function(){
var index = Router.current().params.index;
var a = Text.findOne({index: myindex}).title;
console.log(a);
return a;
without methods
I'm having a issue where calling the $http request is not updating the DOM for jQuery function call.
Please check the Plunker
In my code script.js, for testing purpose I have $scope.components both as global and in a function.
But when i hardcoded the json value to the global variable everything works fine. when i get the json as a $http response, the expand and collapse functionality of tree structure stops working.
Below is my code for $http request
$scope.init = function init() {
$http.get('treeData.json').success(function(data) {
$scope.components = data;
}).error(function(data) {
});
};
Please let me know what am i doing wrong here.
Its because your jQuery code is getting executed before getting the data from treedata.json. $http will create a promise so move you jQuery logic after promises is getting resolved.
I have updated the plunker.
[http://plnkr.co/edit/XuxO29FZUoXf84nVvjrg?p=preview][1]
You should define components first,like below:
$scope.components = [];
Look like the data coming back from the server is not in JSON format. By default $http.get accept JSON return type. Due to this your success function is not getting executed.
Update
You can check the angular documentation for using $http. The recommend way is to use get is as shown below.
$http.get('/someUrl', config).then(successCallback, errorCallback);
try $scope.$apply(
$scope.components = data
);
I use cordova and ionic for my mobile application.
I trying to use $http.get() for loading a JSON web service, so I wrote this code :
.controller('loading', function ($scope, $state, $http) {
var loadedService = {
news: false,
users: false
};
$http.get('http://{url}/users')
.success(function (result) {
loadedService.users = result;
});
I want to change the loadedService.users to returned result from webservice.
But when I trying to console.log(loadedService.users) I will get false (the default value for this variable).
What should I do?
$http.get, or javascript in general is asynchronous. Put console.log inside the success callback of http.get, you will see that (hopefully), you are getting correct results there. But if you put console.log after the get call, you will see the false output since your get call is still busy but your js keeps running. Long story short, js is asynchronous.
So, the important bits:
When I bind a $scope variable in my controller directly to a $resource, angular's automatic promise resolution takes over and my view gets updated as soon as data comes back from the service. However, I need to poll the server for updates on an interval. Attempting to bind to the promise returned from the $interval call is not triggering angular's automatic promise resolution and I do not know if there is something that can be done to get this $interval working against a $resource.
Angular client, ASP.NET WebApi serving JSON, Angular $resource sitting over WebApi. Inside my angular controller, if I bind a $scoped variable to my $resource promises directly, I get results; however, I need my $resources to update on an $interval and I am having problems introducing the interval.
The working, "needs manual refreshing" code (the relevant part, at least)
'use strict';
var ctrls = angular.module('MyModule',[]);
ctrls.controller('FooCtrl', ['$scope', '$location','service',
function( $scope,$location,service)
{
$scope.Bars = service.Bars;
$scope.Baz = service.Baz;
}
var app = angular.module('MyModule.Services',['ngResource']);
app.service('service', ['$resource', function($resource)
{
var proxy = $resource(
'http://server/subsystem/api/Foo/:component',
{},
{
Bars: {method:'GET',isArray:false,url: 'http://server/subsystem/api/Foo/Bars'
,Baz: {method:'GET',isArray:false,rul: 'http://server/subsystem/api/Foo/Baz'
}
return proxy;
}]);
blah blah blah, ng-repeat in my view bound to "bar in Bars" = data to screen.
So now, I go into my controller to implement my interval, and I can get the interval working but the promise does not resolve.
The changes inside the controller (note that I added $interval and $document to my dependency list:
var stop;
$scope.Bars = {};
$scope.Baz = service.Baz; //Note that angular resolves this promise automatically.
$scope.pollService = function(){
if(angular.isDefined(stop))return;
stop = $interval(function(){
if(service){
$scope.Bars = service.Bars;
}
},1000);
};
$scope.stopPolling = function(){
if(angular.isDefined(stop)){
$interval.cancel(stop);
stop = undefined;
}
};
$scope.$on('$destroy',function(){
$scope.stopPolling();
});
$document.ready(function(){
$scope.pollService(); //when I attempt to execute my .then() on pollService(), I get "cannot
//call then on undefined"
});
Edit
Note that the problem I am having is that binding $scope.Bars directly to service.Bars triggers angular's automatic promise resolution but introducing the $interval call does not seem to be. Inspecting $scope.Bars in Chrome Dev Tools indicates that it is an unresolved promise.
I updated my sample code to match what I currently have in my debugger. I have several properties defined on my resource, and I included one that I named 'Baz'. If I assign $scope.Baz = service.Baz directly within my controller, the view binds to it when angular resolves the promise but the one on the interval never does.
You aren't returning the promise from $scope.pollService. Just add return stop; at the end of your function. Also note that the way your API is written, your function will still return undefined if stop is defined.
Therefore, it should probably look like:
$scope.pollService = function(){
if(!angular.isDefined(stop)) {
stop = $interval(function() {
if(service){
$scope.Bars = service.Bars;
}
}, 1000);
}
return stop;
};
stop is also not a very good descriptive name...
EDIT:
From my understanding after reading the docs, you should be invoking the action method on the service like $scope.Bars = service.Bars();. This will return an object that will eventually get populated with data once the promise is resolved. However, I think you also need to be careful here, because if the interval fires faster than the promise resolving rate you might run into issues. I think that you would be better off using $timeout.
So here is what I need to do. I'm using the Angular Deferred Bootstrap library in my code because I need to receive some essential data from a RESTful server before bootstrapping and trying to load the content. Anyway, I must make a second call once the first call resolves. The second call is a login that depends on some URL that is contained in the first response.
Right now, I want to try to make the login call once I receive that data (I was trying in the .success() block) but once the first call resolves, the program begins bootstrapping before the login call is finished; things break because I'm not "logged in" on the server.
window.deferredBootstrapper.bootstrap({
element: window.document.body,
module: 'app',
resolve: {
STARTUP_CONFIG: ['$http', function($http) {
return $http.get(url1 + 'blah/blah/blah/' + layout);
}],
CONTEXT_CONFIG: ['$http', function($http) {
return $http.get(url1 + 'blah/blah/blah/blah').
success(function(data) {
$http.post('https://' + data.url2 + '/10001/guestidentity?client_id=' + data.id).
success(function(result){
token = result.token;
});
});
}],
}
});
Anyone have any idea what I can do?
hi since this is a promise you can chain the next call to the then function something like
$http(url).
then(function(response){
//note that this shouldn't be the body of the response
// but the entire response stream so you need to check for the status code and the body and apply and possibly apply any transformation that is needed as it will probably be just text
// then we can start the other call
return $http(url);
})
this way the second promise will be handled to the final destination.