I am writing a directive in AngularJS and one of the things I need to do is find all of the controls inside a form and iterate over them.
If jQuery was loaded I do this:
var inputs = element.find(".ng-invalid,.ng-valid");
inputs.each(function ()
{
var i = $(this);
});
But since it is not loaded and I just have Angular I can only think to do something like:
var inputs = element.find("input");
//for loop thru this
var selects = element.find("select");
//for loop thru this
//....etc
Is there some better way using jQLite to accomplish this?
If supporting IE7 or older is not a requirement, you can use the querySelectorAll method of Element:
var controls = element[0].querySelectorAll('.ng-invalid, .ng-valid');
[].forEach.call(controls, function (ctl) {
var c = angular.element(ctl);
...
});
Use a for-loop.
var inputs = element.find(".ng-invalid,.ng-valid");
for (var index=0; index < inputs.length; index++) {
var subElement = angular.element(inputs)[index];
// do something with subElement
}
Related
I need to perform a behavior in FrontEnd but I don't know how to do it: Inside the textarea I have to put a background on certain keywords like "+project", "#context", while the user types, as if it were a markup text similar to testing tools for Regex.
Its not the complete solution, but you can adapt this example:
https://jsfiddle.net/julmot/hdyLpy37/
It uses the markjs library:
https://markjs.io/
Here is the javascript code:
// Create an instance of mark.js and pass an argument containing
// the DOM object of the context (where to search for matches)
var markInstance = new Mark(document.querySelector(".context"));
// Cache DOM elements
var keywordInput = document.querySelector("input[name='keyword']");
var optionInputs = document.querySelectorAll("input[name='opt[]']");
function performMark() {
// Read the keyword
var keyword = keywordInput.value;
// Determine selected options
var options = {};
[].forEach.call(optionInputs, function(opt) {
options[opt.value] = opt.checked;
});
// Remove previous marked elements and mark
// the new keyword inside the context
markInstance.unmark({
done: function(){
markInstance.mark(keyword, options);
}
});
};
// Listen to input and option changes
keywordInput.addEventListener("input", performMark);
for (var i = 0; i < optionInputs.length; i++) {
optionInputs[i].addEventListener("change", performMark);
}
I have an automation test that I have switched out most variables for properties which has been working fantastic for me unless i need to chain something. Here is an example of what I'd like it to look like:
var test = module.exports = {
outerElement: element(by.cssContainingText('some.div' 'A name'),
innerElement: $('something.else'),
clickOnaName: function () {
this.outerElement.this.innerElement.click();
},
However I have to use this code because chaining doesn't work the way I am using it:
var outerElement = element(by.cssContainingText('some.div'
'A name');
var innerElement = $('something.else');
var test = module.exports = {
clickOnaName: function() {
outerElement.innerElement.click();
},
Is there a way for me to do chain or should i just leave those elements as variables
please let me know if this helps!
You can use .element(el.locator()) to extend the elements. You can use multiple selectors at once. You can also hit arrays of elements.
Keep in mind that this kind of chaining of protractor selectors is the same as a css space child selector, and not as a > selector.
I.e. $('.parent').$('.child') will select the same elements as in a css file .parent .child, getting ALL children and not just direct children.
module.exports = function(){
this.parent = $('.parent');
this.child = $('.child');
this.childOfParent = parent.element(child.locator());
this.directParentChild = $('.parent').$('.child');
this.parentArray = $$('.parents');
this.child = $('.child');
this.children = parentArray.get(2).element(child.locator());
}
Adding in clicks and such should be pretty straightforward from there, page.childOfParent.click for example.
I am trying to implement two functions in an an angular app but as soon as I implement the filter (start with letters from to), the code stops working. On their own, the (add/delete) functions work but as soon as I turn the data into a factory and try to access with the filter functions it fails.
Working functions:
$scope.items = items;
$scope.deleteItem = function (index) {
items.data.splice(index, 1);
}
$scope.addItem = function (index) {
items.data.push({
Name: $scope.newItemName
});
}
What causes the whole thing to break:
//filtering letters _ NOT WORKING
function setLetters (from, to){
this.fromLetter = from;
this.toLetter = to;
}
//----
$scope.filter.startsWithLetter = function () {
return function (items, fromLetter, toLetter) {
var filtered = [];
for (var i = 0; i < items.length; i++) {
var item = items[i];
var firstLetter = item.Name.substring(0, 1).toLowerCase();
if ((!fromLetter || firstLetter >= fromLetter)
&& (!toLetter || firstLetter <= toLetter)) {
filtered.push(item);
}
}
return filtered;
};
});
//--filtering letters
Full code here: fiddle
There's a few issues in the fiddle. First I'm seeing an "Unexpected token )" error due to an extra ) on line 58.
Then when I fix that there is an issue on line 45 as you are trying to assign a value to $scope.filter.startsWithLetter, when $scope.filter is undefined. I think you want to assign the value to $scope.startsWithLetter.
There is still a problem with the filtering. When filtering with ng-repeat you can specify a filter or simply a predicate function. In each case the arguments passed to the function will be different - please read the docs. The function as-is is designed to be used in a filter created with angular.module('myApp', []).filter(). It doesn't work when you set it on the scope and pass it to filter: as a predicate function. If you prefer to filter using a function on the scope, rather than creating a reusable custom filter, you need to change it to accept the correct arguments - see fiddle.
Your page is trying to access setLetters in $scope.items.data but you are not setting $scope.items.data.setLetters. I don't think it makes sense to set it there inside items.data anyway. Perhaps set it directly on the scope? I also would set fromLetter and toLetter directly on the scope.
I also moved the setLetter buttons inside a <div ng-controller="ItemsController" >
Fiddle with those fixes
for a while I am trying to find how to make a load more(elements from an array) button,using Angular.
I have 9 elements in array, I use ng-repeat to loop them, and limitTo:3 to output first 3.
Questions:
1: is possible to make a load more button using only angular?(load more button is at bottom in example)
2: if not, how to make this work using jQuery?
http://plnkr.co/edit/1gHB9zr0lbEBwlCYJ3jQ
Thanks!
You don't need to think of jQuery, as you could solve this problem easily by using AngularJS itself.
You could maintain a variable inside your controller, name it as limit, then increment the limit variable inside loadMore() function.
Markup
<div ng-repeat="elem in travel.cruise | limitTo:travel.limit" class="cruises">
....COntent here...
</div>
Controller
app.controller('TravelController', function($scope) {
var vm = this;
vm.cruise = cruises;
vm.limit = 3;
$scope.loadMore = function() {
var increamented = vm.limit + 3;
vm.limit = incremented > vm.cruise.length ? vm.cruise.length : increamented;
};
});
Demo Plunkr
You can combine #Pankaj Parkar response with infiniteScroll so you dont need even the button.
No need of cracking any deep coding. Its simple to add just one line of code
<div ng-repeat="elem in travel.cruise | limitTo:travel.limit" class="cruises">
..NG Repeat..
</div>
Controller
$scope.loadMore = function() {
vm.limit = vm.limit + 3;
};
Assigning variable to increase by the 3 or more number will make your code work easily.
Simple and easy trick
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