AngularJS and jQuery alert - javascript

I'm just curious why the following code is working:
<script>
var app = angular.module('VEL',[]);
app.controller('VELControl', function($scope, $http) {
$.getJSON("../json/dict.php?p="+$_GET['p'], function(response){
$scope.velData = response;
});
alert($scope.velData); // also works with alert($scope);
});
But when I remove the alert(...) it stops working.
Could anyone explain me why or refer to a documentation?

The main problem is that you're using jQuery's $.getJSON() method, which Angular doesn't know anything about. Angular is great at tracking when asynchronous events occur and updating the view when they complete, but you have to use Angular's services to take advantage of this.
Angular gives us the $http service for making AJAX calls. Since $http is tied into Angular's digest cycle (read up on this if you've never heard of it, it's critical to understanding how Angular works), Angular will update the view after $http returns data and its callbacks are run.
Rewrite your controller like this and it should work:
app.controller('VELControl', function($scope, $http) {
$http.get("../json/dict.php?p="+$_GET['p']).then(function(response){
$scope.velData = response.data;
});
});
As an aside, your alert() working with the old method was probably a fluke based on how fast your server returned the data. As I understand it, alert() runs asynchronously anyway, so when you called alert($scope.velData), this was probably pushed onto the end of the browser's execution queue, and before it was reached the AJAX call returned and set the value you needed. If your server had been slower, you probably would have alerted undefined and then received your data. If you have control over the server (which it looks like you do), this would probably be a fun experiment to try: put in a sleep(10) or some similar wait to force the server to take a long time; try different values and see what happens with that alert() statement.

Related

DOM update after async call

I am fetching some data from my server and updating the DOM through Angular two way bindning. It is however not working as expected and I need to wrap it inside an ugly setTimeout function for the DOM to have time to update.
$http.post('myBackend.php', someData)
.then(function(res){
$scope.data = res.data;
doStuffWithDOMElements(); // Does not work
});
while this works:
$http.post('myBackend.php', someData)
.then(function(res){
$scope.someDataToPopulateDOMwith = res.data;
setTimeout(function(){ doStuffWithDOMElements();}, 50); // Yup, works
});
The line that gives error without the timeout "Can't read propertly of null" is this:
let y_0 = document.getElementById("l_0").offsetTop;
and in my index.html
<div id="l_{{$index}}" ng-repeat = "x in data"></div>
This is weird. Shouldn't every DOM element that is wrapped up in Angular "events" be updated automatically? $scope.$apply() does not work and shouldn't be necessary either. What's wrong here?
The need of $timeout comes every once in a while in angularjs.Most probably to init a jQuery plugin.
Your Error line:
let y_0 = document.getElementById("l_0").offsetTop;
is because your DOM has not yet been set and you are trying to get the element which yet has to be set or rather rendered in the DOM.
When you use $timeout,it should run after the DOM has been manipulated by Angular, and after the browser renders (which may cause flicker in some cases).That is why it is working in your case when you set the $timeout.
If you want to learn more about digest cycle. Your should know about $evalAsync as well.
If code is queued using $evalAsync from a directive, it should run after the DOM has been manipulated by Angular, but before the browser renders
If code is queued using $evalAsync from a controller, it should run before the DOM has been manipulated by Angular (and before the browser renders) -- rarely do you want this
If code is queued using $timeout, it should run after the DOM has been manipulated by Angular, and after the browser renders (which may cause flicker in some cases).
FurtherMore, Angularjs is a javascript framework. A browser has to do a number of things pretty much all at once, and just one of those is execute JavaScript. But one of the things JavaScript is very often used for is to ask the browser to build a display element. This is often assumed to be done synchronously (particularly as JavaScript is not executed in parallel) but there is no guarantee this is the case and JavaScript does not have a well-defined mechanism for waiting.
The solution is to "pause" the JavaScript execution to let the rendering threads catch up. And this is the effect that setTimeout() with a timeout of 0 does. It is like a thread/process yield in C. Although it seems to say "run this immediately" it actually gives the browser a chance to finish doing some non-JavaScript things that have been waiting to finish before attending to this new piece of JavaScript.
(In actuality, setTimeout() re-queues the new JavaScript at the end of the execution queue. See the comments for links to a longer explanation.)
IE6 just happens to be more prone to this error, but I have seen it occur on older versions of Mozilla and in Firefox.
Also, a lot has been written and explained about why the use of $timeout comes in handy time to time.
Links where you will find good explanation:
Why is setTimeout(fn, 0) sometimes useful?
AngularJS : $evalAsync vs $timeout
Breakup the Operations
One of the things to do is breakup the operations by chaining them.
$http.post('myBackend.php', someData)
.then (function onFulfilled (response) {
$scope.someDataToPopulateDOMwith = response.data;
return response;
}).then (function onFulfilled2 (response) {
doStuffWithDOMElements();
});
This allows the $q service to execute a digest cycle before invoking the second fulfillment handler. The AngularJS framework needs to do a digest cycle so the watch handlers for the ng-repeat directive have an opportunity update the DOM. The ng-repeat directive needs to finish updating the DOM before the doStuffWithDOMElements function can safely manipulate the DOM.
Use $timeout Service
Avoid using the raw browser setTimeout function, instead use the $timeout service. The AngularJS $q service then automatically does digest cycles.
Since the $timeout service returns promises, it can be used for chaining.
$http.post('myBackend.php', someData)
.then (function onFulfilled (response) {
$scope.someDataToPopulateDOMwith = response.data;
return response;
}).then (function onFulfilled2 (response) {
//return $timeout promise
return $timeout(function(){return response}, 1000);
}).then (function onFulfilled3 (response) {
doStuffWithDOMElements();
});
Because calling the then method of a promise returns a new derived promise, it is easily possible to create a chain of promises. It is possible to create chains of any length and since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain. This makes it possible to implement powerful APIs.1
Re-factor the Design
The AngularJS is a MVW* framework.
Avoid manipulating HTML DOM programmatically: Manipulating HTML DOM is a cornerstone of AJAX applications, but it's cumbersome and error-prone. By declaratively describing how the UI should change as your application state changes, you are freed from low-level DOM manipulation tasks. Most applications written with Angular never have to programmatically manipulate the DOM, although you can if you want to.2
Look into modeling what the doStuffWithDOMElements() function does and creating custom directives to watch that model and update the DOM. This fits better with the AngularJS framework and will avoid these timing issues.
in case of DOM manipulation in angular,it is well known.
see this SO link

Ionic angularjs $http module is not get data

In anguler js if I used two times $http in single function so, will it affect the fetching data from web services.
Like :-
.factory('Chats', function($http) {
$http{}
$http{}
});
Thanks
Fetching data using two $http calls is not a problem and they will not interfere with each other because they are called asynchronously.
This is great because it means that they will both be called nearly immediately without one having to wait for the other to complete.
You should know, however, that because of this you could get the results of the second call before you get the results of the first call. This might happen if you were to have a very long call (large amount of data) in your first $http and a very short one (small amount of data). If you want to wait for both to be completed before proceeding then you can use $q.all to wait for both to complete before proceeding. You can read more about this here: https://www.jonathanfielding.com/combining-promises-angular/

Why are busy indicators not displayed when sending requests?

I have some slow OData calls which need to present some sort of visual indicator to the user that something is happening.
I've read the API reference and seen functions like attachRequestSent(), setBusy(), BusyDialog, BusyIndicator, etc.
I tried using them accordingly but did not work for me. The problem seems to be oModel.create causing the whole app to hang while it executes. No loading indicators or anything can run since the app is frozen until the create function has returned.
Edit: I have set up an asynchronous batch read OData call. I have then wrapped the code for handling the received data in a function and called that function inside the success function in the batch call.
This works; the view loads and I see a busy indicator before the fields are populated with the data
oModel.submitBatch(/*fnSuccess*/);
Is this a good way to do it, or is there a better way which is more standard?
Before the Odata call, display the busy indicator (locks the entire app screen). with
sap.ui.core.BusyIndicator.show(0);
Then, in both the success and failure handlers of the odata call, hide it with
sap.ui.core.BusyIndicator.hide();
It does not work with implicit calls (when for instance you bind to an odata model), for this you can use the request sent events, but the idea is the same.
Edit: You also need to give a small delay to allow the indicator to appear, then use setTimeout to call your odata after a small delay.
doStuffWithIndicator: function(){
sap.ui.core.BusyIndicator.show(0);
setTimeout(function(){
doStuff();
sap.ui.core.BusyIndicator.hide();
}, 20);
},
checkout this thread: SAPUI5 Wait for an Deferred-Object // wait for .done() function
Javascript is asynchrone, this means the code will be processed further no matter if you make an call (which might take longer). Therefore before calling an OData Service you need to tell your JS to wait for it (make it synchrone) via an deferred object.
The main problem seems to be oModel.create causing the whole app to hang while it executes. No loading indicators or anything can run since the app is frozen until the create function has returned.
Sounds like you've been using the now-deprecated sap.ui.model.odata.ODataModel the whole time, which sends mostly synchronous XHRs. Synchronous XHRs block the main thread (== UI thread) until the browser receives the response. The browser isn't then able to update the DOM to display the busy indicator during the round trip.
If anyone reading this has the same issue, please migrate to the newer equivalent model: sap/ui/model/odata/v2/ODataModel. It sends only asynchronous requests (AJAX), allowing the UI thread to handle other tasks (such as adding the busy indicator to the DOM) while the browser waits for the response.

Controllers <-> Services interaction and unit-testing : Am I doing this wrong?

I'm building an AngularJS app, and I would it to be respectful or the best practices.
Thus, when I have to call an external $resource or $http request, I do it in an external Service/Factory.
My problem is I don't know what is the best way for retrieving returned value of the Service into the calling Controller.
Currently, what I do is the following :
The controller calls the Service
The Service does the $http call and check the promise.
If promise is successful, the Service $broadcast an event with the returned object.
The Controller catches that event and does operations.
It works great and allows me to use the same event after different requests (ex: In a discussion, after retrieving all messages and after posted myself a message, the same event "new messages to display" is called)
.
But I finally decided to set up testing processes in my application (better late than never), and I realize that I could do it wrong.
--> When unit-testing my Controllers I want to mock some of my Services for returning given values. But as my Controller calls Services which doesn't returns anything (they fire events instead) I think it will be pretty hard and not natural to check Services "return" values. (I can always check if the Service has been called, but not the result).
.
Your opinion, have I done misconception errors ? I consider getting the promises directly into Controllers when calling Services, is this a good (better?) way to do it ?
I aware that each application have its own logic, but I think in AngularJS the number of "working" logics (that preserves modularity, and testability) are particularly restricted to some set of choices and best practices.
Any suggestion would be appreciated.
Without seeing your code, I have my ideas:
Your controller could receive that promise and do what it needs to do. There is no real need to broadcast anything. Your service call $http and then return the promise. When $http promise is resolved, your controller will know and can do what you need to do.
Testing functions in a service that doesn't return anything is not a problem, that is what spyOn is. You can check that the function has been called and that should be enough for your controller. You don't actually care what happens in the service, you cares about your controller (in the test controller, of course).
Please don't use events for such kind of interaction. They make a mess inside of the code and reduce maintainability. Sometimes you can use them but it is fine only for certain operations and for some specific situations. Using promises is good approach.
Here you have three different options:
Just return the $http promise object and handle it inside of your controller
Make your own promise with $q service and place inside of it your own logic for handling the response from $http or direct returning result (it is more flexible approach than the first one)
Just pass to the services a function callback which you can call there inside of your service to pass the result back to controller.
But you definitely don't need to use events in these cases.
What is related to unit-testing you will configure $httpBackend service to mock your queries and in this case everything will work fine and you can test anything you need ($httpBackend). It means that you can inject your service and it works fine (as instead of real http call it will return the object configured as a response in the $httpBackend). So you can still test your controller without need to make anything complicated to your services.

Angularjs - Digest Loop/Repaint timing

having a small issue with AngularJS. See the following:
$http({method: 'GET', url: 'some/url/withJSON.json'})
.success(function(data)
{
$scope.data = data
$animations._load('arg1')
})
$animations is a registered service that does some stuff. Occasionally (maybe 20%) of the time the _load() method will run before the window has been painted/the digest loop completes, meaning the visualisation hasn't completed. By wrapping _load() with a setTimeout() of 10ms this has resolved it, but is there an implicit/explicit way in AngularJS to execute a callback once the digest loop has finished?
Without more information, I would suggest $timeout(fn) will be the best way to do this. It basically makes sure there's a break after angular completes processing for the browser to render. Roughly it is equivalent to setTimeout(fn, 0), so is what you have already suggested, but it is mockable for testing.
There is also $scope.$evalAsync(fn) that would normally render after the current digest cycle but before the DOM renders. I tend to prefer $evalAsync as it makes things easier for me to reason about, and won't allow other non-angular happenings to occur in the meantime, but it depends which works for you.
This answer has more detailed information about the two methods.

Categories