Gathering a value from a column from each row from a table into an array in protractor - angularjs

Writing e2e tests for an Angular App, but I don't seem to be able to get my head around async programming and promises.
I'm attempting to get the value from each row, from the first column and add that to an array to eventually sort it from high to low to have the highest value.
I'm having some trouble resolving the promise of my rows.each, the errormsg is:
'TypeError: undefined is not a function'
//This function will fetch the highest ID from the columns
this.getHighestScheduleId = (function(){
//Array to collect the ID's in
var idArray = [];
//Collects all the rows in our table
var rows = $$('#schedulesData');
//Goes through each row
rows.each(function(row){
//Collect all the row's elements in rowElems
var rowElems = row.$$('td');
console.log(rowElems[0].getText());
});
});

map() would fit nicely here:
rows.each(function(row) {
var rowElems = row.all('td').map(function (td) {
return td.getText();
});
// resolve the promise to see the output on the console
rowElems.then(function (values) {
console.log(values);
});
});

Related

How to check if value already exists?

I have a small app that users can use to search for a movie, and then add it to their watchlist. Currently it is possible to add 1 movie multple times for the same user. Which ofcourse isn't expected behaviour.
My solution would be to find the unique id of the movie that's being added and crosscheck that with my movies_users data. If the movie_id value exists, do this, else do this.
At the moment I do have the unique movie id of the movie that's being added,
$scope.movieListID = response;
console.log ($scope.movieListID.id)
Which gets ouputted like a string, like so,
314365
And I got the movie records from the current user,
$scope.moviesCheck = response.data;
console.log ($scope.moviesCheck)
Which looks like this,
[{"id":2,"title":"Black Panther", "movie_id":"284054"}]
So what would be a good way to check if the result from $scope.movieListID.id already exists in the $scope.moviesCheck data?
* update *
Trying a suggestion below does not give the expected result.
var exists = function (id) {
$scope.moviesCheck.forEach(function (movie) {
console.log (movie.movie_id)
console.log (id)
if (movie.movie_id === id)
console.log ('duplicate')
else
console.log ('not duplicate')
});
}
exists($scope.movieListID.id);
The console.log output from this is,
312221
312221
not duplicate
Which clearly are duplicate results.
You can add a function in your controller to check if the movie exists in the list
var exists = function (id) {
$scope.moviesCheck.forEach(function (movie) {
if (movie.id === id)
return true;
});
return false;
}
// and call it
exists($scope.movieListID.id); // true or false
I'm not 100% if this is a good way to do this, but for me it works and I think it's pretty low on performance,
movieService.loadMovies().then(function(response) {
$scope.moviesCheck = response.data;
var arr = $scope.moviesCheck
function myIndexOf(o) {
for (var i = 0; i < arr.length; i++) {
if (arr[i].movie_id == o.exisitingMovie_id) {
return i;
}
}
return -1;
}
var checkDuplicate = (myIndexOf({exisitingMovie_id:movie.id}));
if (checkDuplicate == -1) {
From your question I've understood that, based on the object exists using id in the array of object, you have to do different action.
You can use $filter for this. Inject the filter for your controller and assign it to the scope. So this will be available whenever you want in this controller.
$scope.cFilter('filter')($scope.movies, {movie_id:$scope.existingMovie.movie_id}, true);
$sope.movies - is the list of movies passed to the filter. You can
send any list based on your need.
{movie_id:$scope.existingMovie.movie_id} - This one is the object
which one we need to find. This can be based on your need. Since we
are searching movie_id, we need to send the object with property
and value. {movie_id:$scope.existingMovie.movie_id}, Here movie_id is
the property and followed by the value with the colon.
true: This indicates that, to search exact matched values. By default
this is false. If this is set to false, then if we want to search 54
in the movie id, this will returns the objects whichever contains 54
as part of the value.
app.controller('controller', ['$filter',function($filter){
$scope.cFilter= $filter;
$scope.Exists=function(){
$scope.movies=[{"id":2,"title":"Black Panther", "movie_id":"284054"},{"id":3,"title":"Black Panther", "movie_id":"32343"},{"id":4,"title":"Black Panther", "movie_id":"98863"}]
$scope.existingMovie={"id":3,"title":"Black Panther", "movie_id":"32343"};
var _obj=$scope.cFilter('filter')($scope.movies, {movie_id:$scope.existingMovie.movie_id}, true);
if(_obj && _obj[0])
{
Console.log('object exists')
}
else
{
Console.log('Object is not found')
}
}
}])
Many Thanks Jeeva Jsb. This got me on the right track, however I thought I would clarify with a practical example that seems to work as expected.
So I have a function called getData which get the AJAX array but we need to check if the record exist before added to scope (else we get duplicates)
if (d.data.length) {
for (var i = 0; i < d.data.length; i++) {
var doesExist = $filter('filter')($scope.notifications, {NotifId:d.data[i].NotifId}, true);
if (doesExist.length == 0){
$scope.notifications.push(d.data[i]);
}
}
}
This should look familier...
when we are iterating through the returned AJAX object we need to check the ID of the (in my case notificiation)
var doesExist = $filter('filter')($scope.notifications, {NotifId:d.data[i].NotifId}, true);
This line creates a new array by filtering the existing array in scope ($scope.notifications) and passing in the same value from you interation.
If the value exists the object will be copied to the new array called doesExist.
A simple check of length will determine if the record needs to be written.
I hope this helps someone.

Using Angular to append next set of results

I have data that i'm getting from an external API via jsonp.
I have requested the first 10 results. This comes back as an object.
I have new call with a button to fetch the next 10 results from it's cache but it just overwrites the first set of returned data.
How can I get Angular to append or push each subsequent data to the bottom?
$scope.getEanApi = function(){
devaFactory.searchRequest()
.then(function(data){
$scope.hotels = data.HotelListResponse.HotelList;
}).finally(function(){
});
return;
};
$scope.getEanApi();
$scope.moreResults = function(){
devaFactory.moreResults()
.then(function(data){
$scope.hotels = data.HotelListResponse.HotelList;
angular.extend($scope.hotels,$scope.hotels);
}).finally(function(){
});
return;
};
If it's an array then do as below:
$scope.data = $scope.data.concat(newData);
However it changes array reference with merged array so if you've used ng-repeat then it will redraw for whole array. If you're using any animation then you may want to redraw for newly elements only so for that append your new array data manually by iterating through as below.
for(var j=0;j<newData.length;j++) {
$scope.data.push(newData[j]);
}
If it's object then do
angular.extend($scope.data,newData);

get nested object length in an angularFire array in Firebase

I have a Firebase structure like this:
user {
uid {
Lessons {
lid1 {
Title: ...
}
lid2 {
Title: ...
}
}
}
}
I want to use AngularFire to convert user as array so I can filter them using Angular like this:
var usersRef = new Firebase($rootScope.baseUrl + "users");
var userListfb = $firebase(usersRef).$asArray();
The problem is, I also need the number of child of the Lessons object. When I log the userListfb, it is an array. But inside the array, the Lessons node still an object. I can not user length to get its length. What is the correct way to find out the number of child of the Lessons Node with Firebase AngularFire?
Edit 1
According to Frank solution, I got an infinite loop (digest circle error from AngularJS).
The problem is, I will not know the "uid" key. I need to loop it in the first array to get the uid into the second firebaseArray.
Let's say I have a ng-repeat="user in users" in the view and call this on view level in each repeat:
{{getLessonLength(user.uid)}}
Then in the controller, I have this function:
$scope.users = $firebaseArray($scope.usersRef);
$scope.getLessonLength = function (uid) {
var userRef = $rootScope.baseUrl + "users/" + uid + "/lessons/";
var lessonsNode = $firebaseArray(new Firebase(userRef));
return lessonsNode.length;
}
}
And it throw this error: Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: []
All I want it is something like var lessonsCount = snapshot.child('lessons').numChildren() in regular Firebase .on('child_added' ...), the numChildren() function in FirebaseArray. Please help!
AngularFire contains quite some code to ensure that an ordered collection in your Firebase maps correctly to a JavaScript array as Angular (and you) expect it.
If you have a reference to a specific user, you can just create a new sync ($firebase) and call $asArray on that.
var usersRef = new Firebase($rootScope.baseUrl + "users");
var userListfb = $firebase(usersRef).$asArray();
var uid1LessonsRef = userRef.child('uid1').child('Lessons');
var uid1LessonsArray = $firebase(uid1LessonsRef).$asArray();
uid1LessonsArray.$loaded().then(function(arr) {
console.log('Loaded lessons, count: '+arr.length);
});
The data will only be synchronized once, no matter how many references you create to it.

protractor return array of values from repeater

I am looking for an easy way to return an array of values from protractor's all.(by.repeater)
Basically, I just want an easy way to make an array of usernames given a repeater like user in users.
Right now I'm building it like this:
allUsers = element.all(by.repeater('user in users').column('user.username')).then(function(array){
var results = []
var elemLength = array.length
for(var n = 0; n < elemLength; n++){
array[n].getText().then(function(username){
results.push(username)
})
}
return results
});
expect(allUsers).toContain(newUser)
Is there a more concise, reusable way to do this built into protractor/jasmine that I just can't find?
AS alecxe said, use map to do this. This will return a deferred that will resolve with the values in an array, so if you have this:
var mappedVals = element.all(by.repeater('user in users').column('user.username')).map(function (elm) {
return elm.getText();
});
It will resolve like this:
mappedVals.then(function (textArr) {
// textArr will be an actual JS array of the text from each node in your repeater
});
I've successfully used map() for this before:
element.all(by.repeater('user in users').column('user.username')).map(function (elm) {
return elm.getText();
});
When I researched the topic I took the solution from:
protractor - how to get the result of an array of promises into another array

Returning values of a fetch

Please look at the code below. It's a Backbone/Parse code that uses some underscore features.
I'm trying to iterate over an Parse class to retrieve "firstName" attributes of all objects in that class.
I have 2 issues with it.
The first one, as indicated with the comment, is that it correctly retrieves the first names, but it duplicates them. So if there are 5 objects, it will retrieve 5 firstName * 5. There is an iteration problem here. This is shown with the console log.
Second problem, is that I try to push firstName values into an array, then return it, so I can use the values later in code using the testt variable. But checking the testt content with a console log sends a message instead of the firstname lists.
Do you see anyway how to fix this code ?
var DoopizCollection = Parse.Collection.extend({
model: Subscribers
}
);
var doopizlist = new DoopizCollection();
var testt;
testt = doopizlist.fetch({
success: function(doopizlist) {
var results = [];
doopizlist.each(function(object) {
results.push(doopizlist.pluck('firstName'));
console.log(doopizlist.pluck('firstName')); // logs 2 duplicate arrays
});
return results;
},
error: function(doopizlist, error) {
console.log("error"); // The collection could not be retrieved.
}
});
console.log(testt); // logs "a b.promise...." message instead of firstNames
The duplication issue is because you are looping over doopizlist twice, once with each and again with pluck. Pluck is just basically shorthand of the map method.
The second issue is, you are expecting testt is the resulting value, when actually it is an instance of jqXHR, which is something known as a promise. So you can use the then method to log the value of the result.
var DoopizCollection = Parse.Collection.extend({
model: Subscribers
}
);
var doopizlist = new DoopizCollection();
var testt;
testt = doopizlist.fetch({
success: function(results) {
return results.pluck('firstName');
},
error: function(results, error) {
console.log("error"); // The collection could not be retrieved.
}
});
testt.then(function(results) {
console.log(results);
});

Resources