Looping collection.fetch in backbone.js a certain number of times - backbone.js

I am trying to fetch a collection x times, incrementing a counter each time. The issue I am having is that since I need to render a view in the callback, and since .fetch() is asynchronous, I cannot simply perform a javascript loop. I would like to somehow tell it in the success callback to call itself again, without getting into an infinite loop.
Here's what I have:
#collection.fetch
data: {q: letters, i: i}
success:
#renderList()
How can I make this run x times, incrementing i each time, while waiting until #renderList is complete before running it again?
UPDATE
So, I'm making progress - this seems to be working, but it looks terrible:
#collection.fetch
data: {q: letters, i: 1}
success:
#collection.fetch
data: {q:letters, i:2}
success:
#collection.fetch
data: {q:letters, i:3}
Also, the success callback is giving me this warning in console:
Uncaught TypeError: object is not a function
Advice?

Maybe try recursive function like this (it's only example):
recursiveFetch = (collection, successCallback, counter, data) ->
if counter == 0
successCallback()
return
data.i = counter
collection.fetch
data: data
success: -> recursiveFetch(collection, successCallback, counter-1, data)
and call it like this:
recursiveFetch(#collection, => #renderList(), i, {q:letters})

Related

Please I am trying to concatenate string result to a variable in an express router but it returns an empty string

I am trying to iterate through the array get the value and search the database, then concatenate the database result to the string translation
app.get('/translate',function(req,res) {
let translate = '';
['hello','love'].forEach(async (word) => {
let trans = await
NaijaLang.find({"engword": word, "naijalang": "yoruba"});
translate +=" " + trans[0].translation;
//Returns values
console.log(translate)
});
//Returns Empty String;
console.log(translate)
res.send(translate);
});
Because you do some async stuff there, but you send the value synchronously. Basically, this code will run in this order:
run let translate='';
run ['hello','love'].forEach(...)
run await NaijaLang.find(...) asynchronously for word=hello
run await NaijaLang.find(...) asynchronously for word=love
run console.log(translate) and res.send(translate);
resolve the value of await NaijaLang.find(...) -> this is the time when the translate is updated for the first time (either for word=hello or word=love. Whatever finishes earlier)
resolve the value of second call await NaijaLang.find(...) -> this is the time when the translate is updated for the second time. But the value was already send in the 5th step.
You can find more detailed explanation here: https://blog.lavrton.com/javascript-loops-how-to-handle-async-await-6252dd3c795
And you can also find there how to fix it. You can use the for-of loop instead of forEach:
app.get('/translate',function(req,res){
let translate='';
for (let word of ['hello','love']) {
let trans=await NaijaLang.find({"engword":word,"naijalang":"yoruba"});
translate+=" " + trans[0].translation;
//Returns values
console.log(translate)
}
//Returns Empty String;
console.log(translate)
res.send(translate);
});
This time, the code will execute as you probably want. First, the find method will be called for word=hello, then, after the execution is finished, the find method will be called for word=love and finally after both calls are finished, the res.send will be called.

Protractor returning a value from a promise

I'm writing a test to see if my code is removing a level by looking at a text value on the screen which holds the count of levels.
it 'allows deleting level versions', ->
browser.get('/api#/costings')
element(By.id("edit")).click()
startCount = element(By.id("versions_count")).getText().then( (count) ->
return count
)
element(By.id("versions")).click()
first=element.all(By.id("listing")).first()
first.element(By.id("delete")).click()
helper.accept_dialog()
element(By.id("back")).click()
expect(element(By.id("versions_count")).getText()).toEqual(startCount - 1)
Problem here is startCount results in a function. I cannot seem to get startCount into an integer so that I can see if the count has gone down by 1 item.
It gives me the error;
1) edit an existing costing allows deleting level versions
Message:
Expected '1' to equal NaN.
If I try parseInt(startCount) I get the same error.
The variable startCount is a promise, and so startCount - 1 doesn't make sense: there is no automatic type conversion from a promise to its resolved value, so you can't subtract one from it.
What you can do, is create a promise whose resolved value is the expected versions count:
expectedCount = element(By.id("versions_count")).getText().then( (count) ->
return (count - 1).toString();
)
and then you can pass this promise to toEqual, as it automatically unwraps promises at the appropriate point in the control flow
expect(element(By.id("versions_count")).getText()).toEqual(expectedCount)
it("Verify Add Element", function(){
basePage.gotoAssetsPage();
var numberOfElementsBefore = assetsPage.getTotalAssertsNumber();//get text from counter
assetsPage.createAsset(); // add one object on the page
var numberOfElementsAfter = assetsPage.getTotalAssertsNumber(); // get text from counter after creation of object
numberOfElementsBefore.then(function(startNumberText) {
console.log("Number of Asserts before test is " + startNumberText);
return startNumberText;
});
numberOfElementsAfter.then(function(endNumberText) {
console.log("Number of Asserts after test is " + endNumberText);
expect(assertsBefore).toBe((endNumberText-1).toString());
});
});
In this test I'm verifying, that quantity of elements before text will equals quantity of elements after test minus 1.
If I not use .toString(), test result will be: Expect '21' to be 21. So, converting to string works. Maybe someone have a better solution :)

AngularJS adds extra &

I want to pass a stringified JSON as an argument but Angular keeps adding extra & at the end of my query. Which is causing me headache.
Here is how I call it:
$scope.products = ProductSvc.getProductsList('{branchId:{"$exists":"true"}}');
which in turn calls:
ProductRes.q({'queryStr':params});
And here is how q is defined:
var ProductRes = $resource('/products/:productId',
{ 'productId':'#productId','queryStr':'#query' },
{
'$save':{method:'POST', params:{'productId':'#productId'}, isArray:true},
'$get':{method:'GET', params:{'productId':'#productId'}},
'q':{method:'GET', isArray:true, url:'/products?:queryStr'},
'$query':{method:'GET', isArray:true, url:'/products'},
}
);
So the problem is, angularJS GETs:
/products?{branchId:{"$exists":true}}& // <- This goddamn ampersand!!
And that extra ampersand is causing my backend to misbehave.
How to get rid of it?

AngularJS e2e Testing: How To Get value of repeater().count()?

Problem
Calling repeater('#myTable tr','Rows').count(); returns a Future, not an integer. I need to get the integer value so I can confirm that an additional row was added to a table.
Code
it('should add a new user when save button is clicked',function()
{
showModal();
//here I'm trynig to store the row count of my table into a local variable.
//a future is returned who's 'value' field is undefined.
var memberCount = repeater('#memberTable tr','Member Rows').count();
//this outputs 'undefined'
console.log(memberCount.value);
input('editedMember.name').enter('John');
input('editedMember.grade').enter(5);
input('editedMember.ladderPosition').enter(3);
element('#saveMemberButton').click();
sleep(1);
expect(element(modalId).css('display')).toBe('none');
//here is where I want to do the comparison against the above stored memberCount
expect(repeater('#memberTable tr', 'Member Rows').count()).toBe(memberCount.value + 1);
});
Test Result
Chrome 25.0 e2e should add a new user when save button is clicked FAILED
expect repeater 'Member Rows ( #memberTable tr )' count toBe null
/Users/jgordon/learning/chessClub/web-app/test/e2e/scenarios.js:45:3: expected null but was 6
Chrome 25.0: Executed 2 of 2 (1 FAILED) (1 min 4.117 secs / 1 min 3.773 secs)
Drilling into the source code for Angularjs' e2e support reveals that you have to call execute() on the Future to have it populate its value. Also, when you call execute you have to provide a "done" function to the execute() otherwise Testacular will (oddly enough!) skip your test.
Code
var rowCountFuture = repeater('#memberTable tr','Member Rows').count();
rowCountFuture.execute(function(){
});
var memberCount = rowCountFuture.value;
While I'm jazzed to see this works, I'm concerned there may be some asynchronous bugs that could come out of this, also, I feel like this is a hack and not the right way to do it. Any ideas?
Based on the latest Protractor version:
it('should add a new user when save button is clicked', function() {
var memberCount;
element.all(by.repeater('#memberTable tr','Member Rows')).count().then(function(value) {
memberCount = value;
});
...
// then do all your entering user info, saving etc.
...
browser.refresh(); // or however you want to load new data
expect(element.all(by.repeater('#memberTable tr','Member Rows')).count()).toEqual(memberCount + 1);
});
I've run into the same issue, and have seen confusing results when testing value returned after calling execute(). I've found this method works more reliably:
var getCount = repeater('ul li').count();
getCount.execute(function(value) {
expect(value).toEqual(3);
});
You can do this most easily in the async promise returned by the locator
element.all(By.repeater 'thing in things').then(function(elements){
count = elements.length;
expect(count).toEqual(3);
});

Returning a value from a function CoffeeScript

I have the code:
country: (origin) ->
#geocoder = new google.maps.Geocoder
#geocoder.geocode(
'latLng': origin,
(results, status) =>
if status is google.maps.GeocoderStatus.OK
return results[6]
else alert("Geocode was not successful for the following reason: " + status);
)
I am calling it in backbone.js as:
test = #country(origin)
console.log(test)
As a test I am using the console.log. However I am getting an:
undefined
response, as the country function is not returning anything. I know that results[6] has data in it, as I can do a conolse.log there and it returns.
How can I make the country function return result[6] when called?
I don't know that API, per se, but it looks like it's asynchronous, which means you cannot get the function to return the value. Instead, you'll have to pass in a continuation function that deals with the result when it becomes available.
country: (origin, handleResult) ->
#geocoder = new google.maps.Geocoder
#geocoder.geocode(
'latLng': origin,
(results, status) =>
if status is google.maps.GeocoderStatus.OK
handleResult(results[6])
else alert("Geocode was not successful for the following reason: " + status);
)
To use this, simply craft a function that knows what to do with the result and pass it to the country function:
obj.country origin, (result) ->
alert 'Got #{result} from Google'
In CoffeeScript the last expression in the function is returned, as in Ruby.
Here, your returning the result of your console.log
typeof console.log("123")
> "undefined"
I've noticed some people avoid this by putting a single # as the last line, which would just return this instead and avoids some awkward syntax.

Resources