My first stack overflow question....
I'm trying to chain an all statements in protractor but I'm getting the error.
TypeError: Object [object Object] has no method 'all'
I'm looking at the API code on the following page
http://angular.github.io/protractor/#/api?view=ElementArrayFinder.prototype.all
Which indicates that you can use element.all(locator).all(locator)
it gives this as an example
var foo = element.all(by.css('.parent')).all(by.css('.foo'))
my code seems to be very similar, and I'm confused why I'm getting this error. I've tried structuring the code exactly like they have it on the API example. I've also tried doing element.all(locator).element.all(locator).
My GOAL here is to take a Ng-repeat of AREFS; find the one that has the text equal to r_string (which is a string generated earlier and added to the page; expect that element to exist; click that element;
Some Attempts:
var parent = element.all(by.repeater('labgroup in LabGroupService.allLabGroups'));
var child = parent.all(by.xpath('//option[text() = \'' + r_string + '\']'));
expect(child.count()).toBe('1');
and
var elem = element.all(by.repeater('labgroup in LabGroupService.allLabGroups')).all(by.xpath('//option[text() = \'' + r_string + '\']'));
expect(elem.count()).toBe('1');
Finally here is a snippet of the HTML i'm working with.
<a ui-sref="root.user-management.labgroup({labgroupID: labgroup.id})" class="ng-binding" href="#/management/labgroup/43">1kvub4wgCvY9QfA</a>
</dd><!-- end ngRepeat: labgroup in LabGroupService.allLabGroups --><dd ng-repeat="labgroup in LabGroupService.allLabGroups" class="ng-scope">
<a ui-sref="root.user-management.labgroup({labgroupID: labgroup.id})" class="ng-binding" href="#/management/labgroup/47">3PNsny8lUMlMwBw</a>
</dd><!-- end ngRepeat: labgroup in LabGroupService.allLabGroups --><dd ng-repeat="labgroup in LabGroupService.allLabGroups" class="ng-scope">
<a ui-sref="root.user-management.labgroup({labgroupID: labgroup.id})" class="ng-binding" href="#/management/labgroup/42">c3NOI7Z3933ui3a</a>
</dd><!-- end ngRepeat: labgroup in LabGroupService.allLabGroups --><dd ng-repeat="labgroup in LabGroupService.allLabGroups" class="ng-scope">
Edit----------------------------------------------------------------------------------------
I'm starting to wonder if this is a version error or maybe a protractor error. In an attempt to debug I've literally included the source code from the API page.
<div id='id1' class="parent">
<ul>
<li class="foo">1a</li>
<li class="baz">1b</li>
</ul>
</div>
<div id='id2' class="parent">
<ul>
<li class="foo">2a</li>
<li class="bar">2b</li>
</ul>
</div>
and the example from the source page.
var foo = element.all(by.css('.parent')).all(by.css('.foo'))
expect(foo.getText()).toEqual(['1a', '2a'])
I'm still getting that same error.
TypeError: Object [object Object] has no method 'all'
Edit 2-------------------------------------------------------------------------------
I managed to solve this issue by adding a 'data-class = labgroup-link' into the actual html code and by using this protractor code.
element.all(by.css('[data-class="labgroup-link"]')).filter(function(elem, index) {
return elem.getText().then(function(text) {
return text === r_string;
});
}).then(function(filteredElements) {
expect(filteredElements[0].isPresent()).toBe(true);
filteredElements[0].click();
ptor.sleep(100);
});
Solution ----------------------------------------
Had to upgrade protractor to get the latest API.
Protractor >= 1.3.0
Should work given: https://github.com/angular/protractor/blob/f7c3c370a239218f6143a/lib/protractor.js#L177
var foo = element.all(by.css('.parent')).all(by.css('.foo'));
Protractor < 1.3.0
ElementArrayFinder doesn't have an all method: https://github.com/angular/protractor/blob/master/docs/api.md#api-elementarrayfinder-prototype-get therefore:
TypeError: Object [object Object] has no method 'all'
Perhaps you want to
var foo = element(by.css('.parent')).all(by.css('.foo'));
// or shorter
var foo = $('.parent').$$('.foo');
Insetad of doing
var foo = element.all(by.css('.parent')).all(by.css('.foo'));
Related
Retreiving filteredItems from ng-repeat in controller through $scope doesn't work
I am trying to get filtered items from ng-repeat but I get undefined when console logging the value of $scope.filteredItems. I even tried the solution provided in Can't get the filtered items from my filtered list inside a modal but it didn't work either.
The ng-repeat is in a uib-typeahead custom popup templates for typeahead's dropdown. I am trying to get the filtered value in the parent controller.
example-input.component.ts
/////////////////////////////////
componentController.$inject = ['$scope', 'exampleDataService'];
function componentController($scope, exampleDataService) {
var someInput = this;
someInput.typedText = '';
someInput.onKeyPress = onKeyPress;
return;
/////////////////////////////////
//controller implementation detail
/////////////////////////////////
function onKeyPress(){
console.log($scope.filteredItems); // prints undefined
}
}
UIB-Typeahead Custom Template for Popup
<script type="text/ng-template" id="input-custom-template.html">
<ul class='dropdown-menu' ng-show='isOpen() && !moveInProgress'
ng-style="{top: position().top+'px', left: position().left+'px'}"
role='listbox' aria-hidden='{{!isOpen()}}' match-limit in-view-container>
<li style="max-height:0;overflow:hidden"><span in-view="$inview && matchLimit.reset()"> </span></li>
<li ng-repeat='match in (filteredItems = (matches | someMatchSort:query | limitTo:matchLimit.value)) track by some.model.example '
ng-class='{active: isActive($index) }'
ng-mouseenter='selectActive($index)'
ng-click='selectMatch($index, $event)'
role='option' id='{{::match.id}}'
in-view="!$inview && isActive($index) && scrollTarget.scrollIntoView()"
scroll-target
>
<div uib-typeahead-match index='$index' match='match'
query='query' template-url='templateUrl'>
</div>
</li>
</ul>
</script>
<input type="text" ng-model="someInput.typedText" typeahead-min-length="1"
uib-typeahead="option.example as option.value for option in someInput.options | filter:{value:$viewValue}"
class="form-control input-text-example icon-search"
placeholder="example"
typeahead-popup-template-url="input-custom-template.html"
ng-keypress="someInput.onKeyPress()"/>
If you need any additional info pls let me know. There is also a filter called someMatchSort.filter.ts but I'm not sure if it makes difference to add it here. Anyways let me know, I will update my question
You might want to use ng-keyup in your case.
Then in your component write like so
function onKeyPress(){
console.log($scope.$$childHead.matches.length);
}
I have created a basic tool using vanilla javascript/html/css to perform certain functions across all emails associated with the business. To maintain the code base simplicity, i have started to porting the application using angularJS, creating modules/controllers etc. However, i got stuck with below situation. It looks like from the logs that my included html template gets called before data is ready from the controller.
I am placing an ajax call from the server which roughly takes ~1 sec. But by the time, I guess Angular executes the included template and because of no model availability, it renders nothing on the application side.
A look into my code:
index.html
<div class="tab-pane active fade in" id="results">
<section ng-controller = "messageSelectionController" ng-include src="'templates/messageSelectionTemplate.html'"></section>
</div>
messageSelectionController.js
app.controller('messageSelectionController',function($scope,$log){
//Generic ajax call
var makeRequest = function(paramObj,successCallback,errorCallBack,url,type){
$.ajax({
method : type ? type : "GET",
url : url ? url : "https://xx.xx.xx/xx/xx",
data : paramObj,
xhrFields : { withCredentials : true }
}).then(successCallback,errorCallBack);
};
var messageSelectionSuccessHandler = function(data){
$scope.messageselectionlist = JSON.parse(data).response.docs;
$log.info($scope.messageselectionlist);
};
var messageSelectionErrorHandler = function(data){
$log.info(data.responseText);
};
var data = {
q : "",
fq:[
'View:xx',
'Platform:xx'
],
start : 0,
rows : 9999
};
makeRequest(data, messageSelectionSuccessHandler,messageSelectionErrorHandler);
});
messageSelectionTemplate.html
<ul>
<li ng-repeat="item in messageselectionlist">
<article class="">
<span class="label label-success">MessageSelection</span>
<a> "Platform : {{item.Platform}} | View : {{item.View}} | Locale : {{item.Locales[0]}}"
<i class = "ocon-flag-{{item.Locales[0] | lowercase }}"></i>
</a>
<span class = "label label-inverse">{{item.Description}}</span>
</article>
</li>
</ul>
I tried to put logs to see when the messageSelectionTemplate is loaded, using below code:
$scope.$on('$includeContentLoaded', function(event, target){
console.log(event); //this $includeContentLoaded event object
console.log(target); //path to the included resource, 'snippet.html' in this case
});
and here's the logs:
-----templates/messageSelectionTemplate.html angular.js:13424
-----Array[654]
Can someone let me know how to defer the included template loading until the modal for template is not available. Thanks!
I am using knockout and I want to compare if a value is unavailable or not. Is there a way in JavaScript/knockout I can do this?
<!-- ko if: User().details()[0] != undefined -->
<p>Your defined</p>
<!--/ko-->
I have tried undefined but that doesn't work. Anybody know what I can compare it with so it checks if the value is unavailable or not?
You need to compare the typeof the value with 'undefined'. For example:
<!-- ko if: typeof User().details()[0] !== 'undefined' -->
<p>Your defined</p>
<!--/ko-->
Please see the MDN documentation for this type of check.
And a snippet that illustrates this:
var x = {
y: 50
};
document.getElementById('output').innerHTML = 'x.y is undefined: ' + (typeof x.y === 'undefined') + '; x.z is undefined: ' + (typeof x.z === 'undefined');
<div id="output"></div>
well instead of checking weather something is undefined and If you are in a dilemma always use with (container-less in your scenario) which servers the exact purpose .
View :
<!-- ko with:$data.User1 -->
<p data-bind="text:$data"> defined</p>
<!--/ko-->
<!-- ko if:$data.User -->
<p>Your defined</p>
<!--/ko-->
View Model:
var viewModel = {
User: ko.observable('charlie')
};
ko.applyBindings(viewModel);
In view you can see i intentionally used with:user1 still your code wont break it simply stop looking further down . If you try the samething for if you get undefined error .
Well if you really want to go ahead with if then try to use a computed and return true/false based on the conditions you impose . make view cleaner
Working sample fiddle
I'm following John Papa's jumpstart course about SPA's and trying to display a list of customers loaded via ASP.NET Web API the knockout foreach binding is not working. The Web API is working fine, I've tested it on it's own and it is returning the correct JSON, because of that I won't post the code for it. The get method simply returns one array of objects, each with properties Name and Email. Although not a good practice, knockout is exposed globaly as ko by loading it before durandal.
I've coded the customers.js view model as follows
define(['services/dataservice'], function(ds) {
var initialized = false;
var customers = ko.observableArray();
var refresh = function() {
return dataservice.getCustomers(customers);
};
var activate = function() {
if (initialized) return;
initialized = true;
return refresh();
};
var customersVM = {
customers: customers,
activate: activate,
refresh: refresh
};
return customersVM;
});
The dataservice module I've coded as follows (I've not wrote bellow the function queryFailed because I know it's not being used)
define(['../model'], function (model) {
var getCustomers = function (customersObservable) {
customersObservable([]);
var options = {url: '/api/customers', type: 'GET', dataType: 'json'};
return $.ajax(options).then(querySucceeded).fail(queryFailed);
function querySucceeded(data) {
var customers = [];
data.forEach(function (item) {
var c = new model.Customer(item);
customers.push(c);
});
customersObservable(customers);
}
};
return {
getCustomers: getCustomers
};
});
Finaly the model module was built as follows:
define(function () {
var Customer = function (dto) {
return mapToObservable(dto);
};
var model = {
Customer: Customer
};
return model;
function mapToObservable(dto) {
var mapped = {};
for (prop in dto)
{
if (dto.hasOwnProperty(prop))
{
mapped[prop] = ko.observable(dto[prop]);
}
}
return mapped;
}
});
The view is then simply a list, it is simply:
<ul data-bind="foreach: customers">
<li data-bind="text: Name"></li>
</ul>
But this doesn't work. Any other binding works, and I've looked on the console window, and it seems the observable array is being filled correctly. The only problem is that this piece of code doesn't show anything on screen. I've reviewed many times the files but I can't seem to find the problem. What's wrong with this?
You can use the knockout.js context debugger chrome extension to help you debug your issue
https://chrome.google.com/webstore/detail/knockoutjs-context-debugg/oddcpmchholgcjgjdnfjmildmlielhof
Well, I just spent a lot of time on an local issue to realize that the ko HTML comment format, if used, should be like this:
<!-- ko foreach: arrecadacoes -->
and NOT like this:
<!-- ko: foreach: arrecadacoes -->
: is NOT used after ko...
I know this question is a little old but I thought I'd add my response in case someone else runs into the same issue I did.
I was using Knockout JS version 2.1.0 and it seems the only way I can get the data to display in a foreach loop was to use:
$data.property
so in the case of your example it would be
$data.Name
Hope this helps
I don't see anywhere in your code that you've called ko.applyBindings on your ViewModel.
KO has a known issue while using foreach in a non-container element like the one above <ul> so you have to use containerless control flow syntax.
e.g.
<ul>
<!-- ko foreach: customers-->
<li data-bind="text: Name"></li>
<!-- /ko -->
</ul>
Ref: http://knockoutjs.com/documentation/foreach-binding.html
EDIT: Answered myself, error was because of old version of knockout, always use newest version, and check existing!
i've been following knockouts tutorial, and tried to do something myself, but get the error, even when I basically have the same code.
<ul data-bind="foreach: tasks">
<li>
<input data-bind="value: title" />
</li>
</ul>
<script type="text/javascript">
(function () {
function Task(data) {
this.title = ko.observable(data.contentName);
}
function TaskListViewModel() {
// Data
var self = this;
self.tasks = ko.observableArray([]);
// Load initial state from server, convert it to Task instances, then populate self.tasks
$.getJSON('<%= Url.Action("GetContentList", "TranslateContentMenu") %>',
{
languageId: $('#LanguageIdNameValuePairs').val()
}, function (allData) {
var mappedTasks = $.map(allData, function (item) { return new Task(item) });
self.tasks(mappedTasks);
});
}
var test = new TaskListViewModel();
console.log(test);
ko.applyBindings(new TaskListViewModel());
}())
</script>
The service i am calling, returns this result:
[{"contentId":"1b07790c","contentName":"test"},{"contentId":"1b07790c","contentName":"test"},{"contentId":"1b07790c","contentName":"test"}]
and this is the error, straight out of firebug console:
Error: Unable to parse binding attribute.
Message: ReferenceError: title is not defined;
Attribute value: value: title
You shouldn't get such error because at this time tasks array should be empty and knockout shouldn't generate <li> tag. Make sure that you are initializing tasks array with [] or with nothing not with something like this [""].
Also you can try to initialize tasks with empty Task object:
self.tasks = ko.observableArray(new Task());
The error i got was because i was using knockout 1.2.1, newest version as of today was 2.1.0, upgrading knockout made it work.
As #Artem has pointed out in the comments this does work in the jsFiddle he constructed. The only thing I can put this down to is your badly formed HTML. You wrote:
<ul data-bind="foreach: tasks">
<li>
<input data-bind="value: title" />
</li>
Knockout does not like badly formed HTML. You should try:
<ul data-bind="foreach: tasks">
<li>
<input data-bind="value: title" />
</li>
</ul>