Im very new to AngularJS (4 hours new) and I'm trying to get an http call working, however what it seems like its happening is Angular keeps calling the http get request over and over again. I'm sure this is because my approach is wrong. This is what I'm trying to do.
snippet of my controller file The webservice works fine. I am running this in a node.js app
function peopleController($scope,$http){
$scope.getPeople = function(){
$scope.revar = {};
$http.get('/location/-79.18925/43.77596').
success(function(data){
console.log(data);
$scope.revar = data;
});
}
}
My list.html file
<div ng-controller="busController">
<div class="blueitem">{{getPeople()}}</div>
</div>
I know I will not see the results since im not returing anything in my getPeople Method but I wanted to see the log output of the result which I did see in chrome, but a million times and counting since angular keeps calling that url method over and over again. Instead it keeps hitting.
How do I get angular to return the response just once?
The problem you are experiencing is linked to the way AngularJS works and - to be more precise - how it decides that a template needs refreshing. Basically AngularJS will refresh a template based on a dirty-checking of a model. Don't want to go into too much details here as there is an excellent post explaining it (How does data binding work in AngularJS?) but in short it will keep changing for model changes till it stabilizes (no more changes in the model can be observed). In your case the model never stabilizes since you are getting new objects with each call to the getPeople() method.
The proper way of approaching this would be (on of the possible solutions):
function peopleController($scope,$http){
$http.get('/location/-79.18925/43.77596').
success(function(data){
$scope.people = data;
});
}
and then, in your template:
<div ng-controller="busController">
<div class="blueitem">{{people}}</div>
</div>
The mentioned template will get automatically refreshed upon data arrival.
Once again, this is just one possible solution so I would suggest following AngularJS tutorial to get better feeling of what is possible: http://docs.angularjs.org/tutorial/
Couple of things. Welcome to angularjs, its a great framework. You probably shouldn't be calling getPeople from the webpage. Instead,
function peopleController($scope,$http){
var getPeople = function(){
$scope.revar = {};
$http.get('/location/-79.18925/43.77596').
success(function(data){
console.log(data);
$scope.revar = data;
});
}
getPeople();
}
and then in html
<div ng-controller="busController">
<div class="blueitem">{{revar|json}}</div>
</div>
Also, I would recommend you looking into the ngResource, especially if you are doing CRUD type applications.
Hope this helps
--dan
Related
var gamedataRef = firebase.database().ref('gamedata');
gamedataRef.on('child_added', function(data) {
$scope.abc = data.val().textgame;
});
I have to find this.
Firebase data is loaded asynchronously. This is great, because it allows the browser to continue being responsive while the data is being loaded. But unfortunately it also means that AngularJS is not expecting updates anymore by the time the data comes back.
The solution is to tell AngularJS when the data comes back, with for example $timeout():
var gamedataRef = firebase.database().ref('gamedata');
gamedataRef.on('child_added', function(data) {
$timeout(function(){
$scope.abc = data.val().textgame;
});
});
You might also want to have a look at AngularFire, an open-source binding library that solves this problem and a lot of others you're likely to run into.
I am trying to call an API end point once a user clicks a button holding a myNavigator.pushPage() request. However,I can not get the $scope data generated from the $http.get request to be passed to the new page.
If I test using console.log('test'); inside the .success of the $http.get request I successfully get the log info in the console but any data held in $scope.var = 'something'; does not gets passed to the page! Really confused!
$scope.historyDetails = function(id){
var options = {
animation: 'slide',
onTransitionEnd: function() {
$http.get('http://xxx-env.us-east-1.elasticbeanstalk.com/apiget/testresult/testId/'+id).success(function(data) {
$scope.testscore = 'something'; // this is not getting passed to page!
console.log('bahh'); // But I see this in console
});
}
};
myNavigator.pushPage("activity.html", options);
}
Page:
<ons-page ng-controller="HistoryController">
...
<span style="font-size:1.2em">{{testscore}} </span><span style="font-size:0.5em;color:#555"></span>
...
</ons-page>
Yes, that's so because both pages has different controllers, resulting in different scopes. One can not access variables from one scope to another.
Hence one solution in this case can be using rootScope service.
Root Scope is parent scope for all scopes in your angular application.
Hence you can access variable of root scopes from any other scope, provided that you are injecting $rootScope service in that controller.
to know more about rootScope check this link.
Good luck.
Update 1:
check these articles
http://www.dotnet-tricks.com/Tutorial/angularjs/UVDE100914-Understanding-AngularJS-$rootScope-and-$scope.html
https://toddmotto.com/all-about-angulars-emit-broadcast-on-publish-subscribing/
As Yogesh said the reason you're not getting your values is because if you look at $scope.testscore and try to find where is the $scope defined you will see that it's an argument for the controller function (thus it's only for that controller).
However we can see that the controller is attached to the page and you are pushing another page.
So in that case you have several options:
Use the $rootScope service as Yogesh suggested (in that case accept his answer).
Create your own service/factory/etc doing something similar to $rootScope.
(function(){
var historyData = {};
myApp.factory('historyData', function() {
return historyData;
});
})();
Technically you could probably make it more meaningful, but maybe these things are better described in some angular guides.
If you have multiple components sharing the same data then maybe you could just define your controller on a level higher - for example the ons-navigator - that way it will include all the pages. That would be ok only if your app is really small though - it's not recommended for large apps.
If this data is required only in activity.html you could just get it in that page's controller. For example:
myApp.controller('activityController', function($scope, $http) {
$http.get(...).success(function(data) {
$scope.data = data;
});
}
But I guess you would still need to get some id. Anyway it's probably better if you do the request here, now you just need the id, not the data.
You could actually cheat it with the var directive. If you give the activity page <ons-page var="myActivityPage"> then you will be able to access it through the myActivityPage variable.
And the thing you've been searching for - when you do
myNavigator.pushPage("activity.html", options);
actually the options is saved inside the ons-page of activity.html.
So you can do
myNavigator.pushPage("activity.html", {data: {id: 33}, animation: 'slide'});
And in the other controller your id will be myActivityPage.options.data.id.
If you still insist on passing all the data instead of an id - here's a simple example. In the newer versions of the 2.0 beta (I think since beta 6 or 7) all methods pushPage, popPage etc return a promise - which resolve to the ons-page, making things easier.
$scope.historyDetails = function(id){
myNavigator.pushPage("activity.html", {animation: 'slide'}).then(function(page) {
$http.get('...' + id).success(function(data) {
page.options.data = data;
});
});
});
Side note: You may want to close the question which you posted 5 days ago, as it's a duplicate of this one (I must've missed it at that time).
I am trying to use the ebay api in an angular.js app.
The way the api works by itself is to pass data to a callback function and within that function create a template for display.
The problem that I am having is in adding the data returned from the callback to the $scope. I was not able to post a working example as I didnt want to expose my api key, I am hoping that the code posted in the fiddle will be enough to identify the issue.
eBayApp.controller('FindItemCtrl', function ($scope) {
globalFunc = function(root){
$scope.items = root.findItemsByKeywordsResponse[0].searchResult[0].item || [];
console.log($scope.items); //this shows the data
}
console.log($scope.items); //this is undefined
})
http://jsfiddle.net/L7fnozuo/
The reason the second instance of $scope.items is undefined, is because it is run before the callback function happens.
The chances are that $scope.items isn't updating in the view either, because Angular doesn't know that it needs to trigger a scope digest.
When you use the Angular provided async APIs ($http, $timeout etc) they have all been written in such a way that they will let Angular know when it needs to update it's views.
In this case, you have a couple of options:
Use the inbuilt $http.jsonp method.
Trigger the digest manually.
Option number 1 is the more sensible approach, but is not always possible if the request is made from someone else's library.
Here's an update to the fiddle which uses $http.jsonp. It should work (but at the moment it's resulting in an error message about your API key).
The key change here is that the request is being made from within Angular using an Angular API rather than from a script tag which Angular knows nothing about.
$http.jsonp(URL)
.success($scope.success)
.error($scope.error);
Option 2 requires you to add the following line to your JSONP callback function:
globalFunc = function(root){
$scope.items = root.findItemsByKeywordsResponse[0].searchResult[0].item || [];
console.log($scope.items); //this shows the data
$scope.$apply(); // <--
}
This method tells Angular that it needs to update it's views because data might have changed. There's a decent Sitepoint article on understanding this mechanism, if you are interested.
Background:
I am building my offline application which uses AngularJS for UI and PocuhDB for locally storing the data retrieved from the server.
Issue:
The data retrieved from PouchDB is not getting rendered in the UI.
Controller:
$scope.retrieveView = function (sys, code, majorVer, minorVer) {
var promise;
promise = dataService.getDataFromLocalDb().then(
function(dataFromPouchDb){
$scope.data = dataFromPouchDb.data;
});
return promise;
}
And then in the UI code I have the following :
<h1> {{data}}</h1>
I have debugged the code and everything seem to work fine. But the data is not getting displayed in the UI.
If I hard code a value to the data field then its getting rendered in the UI
$scope.data ="TEST";
This question is kind a old but I just came around it.
Issue is that Angularjs is based on so called digest cycles. When your model or view is changed digest cycle is triggered, watch for changes and update model or view respectively. It is so called two way data binding.
This digest cycle is not triggered periodically on some time base but on events instead. Those events are angular directives like ng-click, ajax calls $http or some other angular events like $timeout. You can find more information about digest here.
In general you should use those things when working with angular application to avoid such situations. In some cases its not possible however like in your case when getting data from DB. Digest cycle is not triggered and your view is not updated by angular.
Workaround for this is manually trigger $digest cycle. Way you have described:
if(!$scope.$$phase) {
$scope.$digest();
}
is working but considered as angular anti-patern and is discouraged by angular team, you should use:
$timeout();
instead. For more information see this answer.
I would maybe consider adding $timeout() call to hook for insert, update, delete hooks or events. Maybe pouchDB sync could be helpfull there.
The code you show seemed correct, maybe you can use console.log() to track the progress of the data. I think the problem might not in this layer. Maybe in the area where you wrapped getDataFromLocalDb(), track and find if the data have transfer to here, or where it disappeared.
The code started to work when i added the following :
if(!$scope.$$phase) {
$scope.$digest();
}
But i have no idea what magic does this code do.
It would be a great help if some some could advice.
The complete code that works now is :
$scope.retrieveView = function (sys, code, majorVer, minorVer) {
var promise;
promise = dataService.getDataFromLocalDb().then(
function(dataFromPouchDb){
$scope.data = dataFromPouchDb.data;
if(!$scope.$$phase) {
$scope.$digest();
}
});
return promise;
}
My problem
I am using Angular to create a Phonegap application. Most of my pages are fairly small and the transition/responsiveness is quick and smooth. However I have one page that is fairly large that I am having an issue with.
The method for changing to this page is straightforward:
<button ng-click="$location.url('/page2')"></button>
When you "tap" the button above it takes about 1-2s to respond and change pages. I have double checked all areas for improvement on this page and determined that the delay is caused by Angular compiling and parsing the DOM of this page prior to changing the page. Please note that I am testing this on a real device so it is not due to emulator speeds.
The question
Is there a way to automatically or manually intercept page changes and put them in a sort of "loading" page so the response to the button click is immediate and page change is visible but the page content loads in a second or 2 later onto this "loading" page.
Its only an issue cause it is very awkward to click something and have nothing happen. I am having a very hard time finding any resources on this matter so if someone can even point me in the right direction to look I would be grateful.
Edit:
A super hacky solution I found was to use an ng-include on wrapper page and delay the include for a little bit.
myBigPageWrapper.html:
<div ng-include="page"></div>
Controller:
$scope.page = '';
setTimeout(function() { $scope.page='/pages/myBigPage.html'; $scope.$apply(); }, 1000);
Then navigate to your wrapper page instead: $location.url('/myBigPageWrapper')
This is obviously not ideal... But I hope this helps clarify what I am attempting to do.
Page2.html
This is the section that causes the page to slow down, commenting this out makes the page load very quickly. There are 13 pages in the "auditPages" array each containing about 50 lines of html mostly containing form input elements. Quite a bit of logic however it runs great once it is loaded. I am not going to include all the pages as it would be overload.
<div class="page-contents">
<form name="auditPageForm">
<div ng-repeat="(pageKey, pageData) in auditPages " ng-show="currentAuditPage.name==pageData.name">
<audit-form page="pageData">
<ng-include src=" 'partials/audit/auditSections/'+pageData.name+'.html'" onload="isFormValid(pageKey)"></ng-include>
</audit-form>
</div>
</form>
</div>
To sum up my comments above:
Your question was:
Is there a way to automatically or manually intercept page changes and
put them in a sort of "loading" page?
A lot of people asks for this question since Angular doesn't seem to provide a nice handling of a loading transition.
Indeed, the possible nicest solution would have been to "play" with the resolve property of angular's module configuration.
As we know, resolve allows to run some logic before the targeted page is rendered, dealing with a promise. The ideal would be to be able to put a loading page on this targeted page, while the resolve code is running.
So some people have nice ideas like this one:
Nice way to handle loading icon while route is changing
He uses $routeChangeStart event, so the loading icon would happen on the SOURCE page.
I use it and it works well.
Also, there is another way: make use of $http interceptor (like #oori answer above), to have a common code allowing to put a loading icon but...I imagine you don't want the same icon on every kind of http request the page does, it's up to you.
Maybe in the future, a solution would come directly associated to the resolve property.
Angular has $httpProvider.responseInterceptors
// Original by zdam: http://jsfiddle.net/zdam/dBR2r/
angular.module('LoadingService', [])
.config(['$httpProvider', function ($httpProvider) {
$httpProvider.responseInterceptors.push('myHttpInterceptor');
var spinnerFunction = function (data, headersGetter) {
angular.element(document.getElementById('waiting')).css('display','block');
return data;
};
$httpProvider.defaults.transformRequest.push(spinnerFunction);
}])
// register the interceptor as a service, intercepts ALL angular ajax http calls
.factory('myHttpInterceptor', ['$q','$window', function ($q, $window) {
return function (promise) {
return promise.then(function (response) {
angular.element(document.getElementById('waiting')).css('display','none');
return response;
}, function (response) {
angular.element(document.getElementById('waiting')).css('display','none');
return $q.reject(response);
});
};
}])