Angularjs chaining .then to depend on each other - javascript

I have a chain of events set in .then statements. I wish I understood a way to use .when() in this case. This function is called on a ngn-click. The issue is that I have to click twice for it to go through. The $rootScope.csa has data going into the function that is used in the .then( functions ). I when in inspect in the chrome debugger the and step through everything works fine I believe it is because the debugger is slowing down the application and allowing it to keep up with its self. Other wise when I go through with out the debugger it goes so fast that it takes two clicks for $rootScope.csa.section.data to be populated for the next function to work as expected. The first two then statement functions are services that are wrapped in a promise and $timeout on there end and the $timeouts do not seem to be delaying the process. I have looked over q.derer() many times but cannot wrap my head around how it would be implemented in this case. Any help or information to get to the needs that I am looking for would ne appreciated.
audit.LockData('section', $scope.csa.ID, user.ID, $scope.csa.Revision)
.then($rootScope.csa = audit.RecordsCheck($rootScope.csa)) // gets data and pupulates it to the $rootscope.csa.section.data
.then($rootScope.csa = audit.GetInstance($rootScope.csa), function(){ return $rootScope.csa}) // gets ID(s) from $rootScope.csa.section.data.instances.ID(s) and populates them to the $rootScope.csa.section.instances
.then(function() {if($rootScope.csa.section.data) $location.path('/Update')})
.then(function() {if($rootScope.csa.section.data) $rootScope.$broadcast('root_updated')
});

You always need to pass a callback function to then, not some call result (even if it is a promise). I have not wrapped my head around what these functions do, but try
audit.LockData('section', $scope.csa.ID, user.ID, $scope.csa.Revision)
.then(function(lockResult) {
return audit.RecordsCheck($rootScope.csa)) // gets data and pupulates it to the $rootscope.csa.section.data
})
.then(function(checkResult) {
return audit.GetInstance($rootScope.csa) // gets ID(s) from $rootScope.csa.section.data.instances.ID(s) and populates them to the $rootScope.csa.section.instances
})
.then(function(instance) {
if ($rootScope.csa.section.data) {
$location.path('/Update')
$rootScope.$broadcast('root_updated')
}
});

Related

perform multiple DOM changes at once

My frontend framework is very asynchronous. It means, that when the user exetutes some action, then the "model" is changed and after that a bunch of callbacks modifying the DOM are called. Changes of the "model" and dependent callbacks are called asynchronously, because some data has to be "consulted" with server. It leads to ugly reflows.
I've experimented and if I make multiple DOM changes in one code block using JQuery, all is done at once, but my ramework needs it to be done asynchronously in independent calls.
What I need is something like transactions in DB world. Before I start to code some batch DOM update library, I want to know if there is not simpler way.
In my case, all changed elements have one parent, so I wish I could tell the browser: "Do not redraw children of this node. Make different changes to all children. Now redraw all children of this node". Can you help me?
I believe there's no API to tell the browser to pause reflow/restyle/rendering so that it doesn't repaint after you returned control to the event loop and are waiting for additional data from the server.
It's hard to present a proof that something doesn't exist, but I think the fact that the HTML spec's "Processing model" doesn't mention anything like this -- only heuristics like "user agent might wish to spend less resources rendering third-party content, especially if it is not currently visible to the user" -- is telling.
Another thought: how would the browser handle API calls that ask for layout/style information on elements that have their rendering paused? Return the stale information corresponding to the last rendering? What about the newly created elements?
I'm posting this answer because you said the focus of this question was about built-in browser support, but I think
charlietfl's answer is the right solution for the problem given how browsers work.
Use promises and do all the dom manipulation after all promises have resolved. Note that $.ajax returns a promise and you can chain multiple ajax calls by returning each in a then() and then finally return what is needed to do the dom manipulation once all of the request promises have resolved
function doStuff() {
return $.post('/echo/json/', {json: '{"item":1}'}).then(data => {
// return function we can call when all requests completed
return function() {
console.log('doStuff() data', data)
}
})
}
function doOtherStuff() {
return $.post('/echo/json/', {json: '{"item":22}'}).then(data => {
// return function we can call when all requests completed
return function() {
console.log('doOtherStuff() data', data)
}
})
}
Promise.all([doStuff(), doOtherStuff()]).then(function(funcs) {
// now all requests complete call each of the returned functions
funcs.forEach(f => f())
})
DEMO
I believe this is the idea of ReactJS. ReactJS has something call virtual DOM (Javascript object) which manages to minimize HTML DOM manipulation.
Learn more about ReactJS https://reactjs.org/

Events after clickMouseButton

In one of my tests I have the mouse moved to a specific location and then clickMouseButton() is called. This action is to change the data that is displayed (paging the data.) However, after the click, when I try to get the text in a column of the table to verify that the data changed the data changes back to what was originally displayed and my test fails. My test code is:
return Remote
.moveMouseTo(JE.Buttons.Scrolling(), 5, 100)
.clickMouseButton()
.end(Infinity)
.sleep(500)
.findByXpath('//*[#id="vwNJEAll_grid_header_tbody"]/tr[2]/td[2]/div')
.getVisibleText()
.then(function (result) {
expect(result).not.to.equal(firstSeenJENumber);
});
The code behind JE.Buttons.Scrolling() is:
return Remote
.setFindTimeout(5000)
.findByXpath('//*[#id="vwNJEAll_grid_table_container"]/div[2]/div');
It really seems to me like the locator in Leadfoot is binding to what is on the page when it first loads. Is this the case, and how should I go about getting what I need to happen? I have no other explanation for this, but hopefully you do.
moveMouseTo doesn’t accept a Promise as its first argument, so that moveMouseTo call is wrong and won’t do what you want. The JE.Buttons.Scrolling() call in your example code also occurs immediately (during command chain construction), it doesn’t occur only once moveMouseTo is actually performed.
To retrieve and use an element with your abstraction function, pass the function to then and then use the element:
return Remote
.then(JE.Buttons.Scrolling)
.then(function (element) {
return Remote.moveMouseTo(element, 5, 100);
})
// …remainder of test…

Angular processing not waiting on http.get .then

I have a an issue where it seems as though my controller is not waiting on the http.get '.then'. I am getting data properly back but it seems as though another function is processing before the data is retrieved. I've gone through many posts and have tried many of the things mentioned in those posts, but it doesn't seem to help. I am using PHP to retrieve the data.
I have a HTML file that calls two functions (I had tried with one, but when that didn't work, I tried splitting up the functionality).
HTML of the calls
<form editable-form name="editableForm" onaftersave="fetch();updateDetailsData()" >
Controller functions
$scope.fetch = function() {
$http.get("api/checkSave/"+ JSON.stringify($scope.programDetails))
.then(function(data) {
$scope.okToSave = data.data.save;
$scope.missFields = data.data.fields;
console.log($scope.okToSave); // line #194
console.log($scope.missFields); // line #195
});
}
$scope.updateDetailsData = function(){
console.log($scope.okToSave); // line #202
}
What displays in the console shows:
undefined // line 202
false // line 194 - correct data
Object // line 195 - correct data
As you can see, it appears to be processing the function updateDetailsData before the fetch function finishes. I thought the then should make processing wait until the get is finished - the promise returned.
I need to do some processing in the updateDetailsData function based on the values in the $scope variables but when it gets there they are undefined.
Can someone help? I'm sure it is something little that I am missing, but I think I have tried just about all solutions provided on these forums and still end up with the same results.
The problem comes from: onaftersave="fetch(); updateDetailsData()".
The update function executes as soon as fetch returns, not as soon as fetch is resolved.
Rework your function a bit:
function fetch () {
return $http.get("api/checkSave/"+ JSON.stringify($scope.programDetails))
.then(function(data) {
// ...
});
}
$scope.fetchAndUpdate = function () {
fetch().then(updateDetailsData);
}
In the template:
<form editable-form name="editableForm" onaftersave="fetchAndUpdate()">
You're using promises, so you have to wire into them, they aren't blocking:
<form editable-form name="editableForm" onaftersave="fetch().then(updateDetailsData)" >
Controller functions
$scope.fetch = function() {
return $http.get("api/checkSave/"+ JSON.stringify($scope.programDetails))
.then(function(data) {
$scope.okToSave = data.data.save;
$scope.missFields = data.data.fields;
console.log($scope.okToSave); // line #194
console.log($scope.missFields); // line #195
});
}
$scope.updateDetailsData = function(){
console.log($scope.okToSave); // line #202
}
Your fetch function is calling $http.get which makes an asynchronous call to your server. What that means is that the call will return right away, it won't block (i.e. stop the code from executing) while it waits for the server to respond. That is why you provide a callback using the .then function.
So taken the way you have currently written it, it is working as designed. If you want to have updateDetailsData function be executed after your then code then you have to either put it inside of the then or chain the functions together like Michael P. Bazos or Matthew Berg suggested.

Meteor Method Endlessly Runs

I have this code in a Meteor.methods definition:
update_field: function(collection,document_id,field,value) {
obj = {};
obj[field] = value;
console.log(obj);
if (collection == 'clients') {
var Collection = Clients;
} else if(collection = 'sites') {
var Collection = Sites;
}
Collection.update(
{
_id: document_id
}, {
$set: obj
}, function(error,id) {
console.log(error,id);
return(error,id);
}
);
}
This method is called from several client-side helpers events, and updates the field as needed. But whenever it runs once, it never stops running. Sometimes it runs infinitely even when all the Meteor.call('update_field')s have been commented out. I have tried including a 'caller' parameter and adding that to all the possible calls to figure out why it keeps getting called to no avail. Any ideas why this is looping?
Edit: this runs 2,000/minute
Edit2: this is called in one of two ways: on a keyup code==13 (enter) in an appropriate field or a field blur. However, event when these calls are commented out, the issue persists.
Especially your second comment worries me:
However, even when these calls are commented out, the issue persists.
Then who is calling it? The behaviour you're describing points to some helper executing the method. The method changes some data, which re-executes the helper (reactivity) and we end up with a classic endless loop.
Check your entire source code for references to this method:
$ grep -r "update_field" *
Maybe you set a variable somehow and then use the variable to call the method. Also: Have you declared the method inside a Meteor.methods({ ... }) block?
I think the issue was that one of my methods blurred the input field but preventDefaulted. Then the blur handler was called and caused the loop from there. This is my first Meteor project, so I'm chalking this one up to not quite understanding the system sufficiently. I still find it strange that the method was getting called when the callers were commented out, but I'll figure that one out another day.

"Function factory" doesn't seem to work, JavaScript

I'm displaying a series of images in a loop, and I'm trying to implement some sort of nudity filter so I'm using nude.js, a library that can somewhat detect nudity. Here's the code:
// we're inside a loop
$(".images").prepend($("<img>").attr({src: whatever, id: uniqueid}).load(function(e) {
nude.load(e.target.id);
nude.scan(function(result) { if (!result) $(e.target).detach(); });
});
However, it detaches all of the wrong images because nude.js is slow and it completes after the loop has gone on to the later iterations, detaching those images instead of the one it was working on.
I've tried using a function factory:
function generateCallback(arg) {
return function(result) { if (!result) $(arg).detach(); };
}
and
nude.scan( generateCallback(e.target) )
but the same thing happens.
What I want is a load event that will remove the image if it seems to contain nudity. How can I do this properly?
EDIT: nude.js works like this:
nude.load(imageid);
nude.scan(callback); // it'll pass true or false into the callback
another edit: accidentally omitted the id setting from the code I posted, but it was there in my real code, so I added it here.
I suspect the case here is that this kind of sequential processing won't work with nude.js.
Looking at the nude.js code, I think your problem is occurring in the call to nude.scan. nude.js has a variable that stores the function to invoke after the scan has completed. When calling nude.scan(callback), this variable is set to be callback.
From your PasteBin, it seems as though the callback gets assigned as expected on the first call, but on the second and subsequent calls, it gets replaced, hence why the second image is detached and not the first.
What happends to your script, is that the e var is global to the function and so after each loop it gets replaced with the new one. So when the first image is scanned, e already became the event of the second image, which get detached.
To solve your problem, use closures. If you want to know more about closures, have a look here.
Otherway, here's the solution to your problem :
$(".images").prepend($("<img>").attr({src: whatever, id: uniqueid}).load(function(e) {
(function(e) {
nude.load(e.target.id);
nude.scan(function(result) { if (!result) $(e.target).detach(); });
}) (e);
});
EDIT: AS nick_w said, there is var that contains the callback and probably gets replaced each time so this is why it isn't the right picture getting detached. You will probably have to modify the script yourself

Categories

Resources