Test for scrolling - angularjs

In my angular application, I have a page that has nav links on the side, that, when clicked, scrolls the page down to a matching element.
How do I write e2e test for this in protractor? Is there something like "Grab the first visible h1" or something like that?

You can use javascript's window.pageYOffset for this.
Here's how I've done it in one of my own test case:
browser.driver.sleep(2000);
browser.executeScript('return window.pageYOffset;').then(function(pos) {
expect(pos).to.be.at.most(100);
});
Where 100 is my expected position.
Note: I'm using mocha and chai instead of jasmine. So, just change the last line accordingly. Also I'm waiting 2 seconds for my scrolling to complete.

You should be able to grab the first h1 element that is in the page with something like this:
$$('h1').first().getText().then(function (h1Txt) {
expect(h1Txt).toEqual('Correct h1 text');
});
or use a specific index to find the right h1:
$$('h1').then(function (h1tagList) {
var h1Text = h1tagList[0].getText();
expect(h1Text).toEqual('Correct h1 text');
});
If it is not visible on the page at the time it tries to read the text it should throw an error like element not reachable or something like that.

You can sipmly use a scroll down function :
var filter = browser.findElement(by.id('idvalue'));
var scrollIntoView = function () {
arguments[0].scrollIntoView();
}
browser.executeScript(scrollIntoView, filter);

Related

$location.hash() breaking my function

I'm very close to finishing up my page, and I ran into this odd bug.
When I use ng-include, the pages load fine, but they resume scrolling from the previous position, so they don't start at the top with every click.
I resorted to using anchor scroll, but it doesn't work properly. I have to click the link again, for it to load the contents, and if I click the same link again, it offsets the page to some weird position.
This is my code in the controller:
$scope.toPage = function (index, id) {
$scope.missiveIndex = index;
$scope.contentsDown();
$location.hash(id);
};
and this is the HTML part:
<article id="{{articles.ids}}" class="stories-anim" ng-repeat="articles in stories" ng-hide="!isMissiveSlideIndex($index)" ng-include="articles.content" [autoscroll]>
</article>
the id is passed in via ng-click="ng-click="toPage($index, button.ids);"
is there a way to put the location.hash in the [onload] expression and autoscroll that way?
thanks
Probably the anchor scroll didn't work because the content of the page was not fully loaded when it was called.
You can solve that by using a timeout.
$timeout(function() {
$anchorScroll();
},500);

view fails to update after executing ngModelController.$render function

Refer to this code:
http://plnkr.co/edit/4QpPZZib6qGjhx85Do0M
Here's how to replicate the error:
1. Run the plnkr
2. then click on any of the buttons "200", "300" etc. You will notice that the model updates just fine. No issue so far
3. Now paste something in the input box. The paste should work just fine.
4. Now try clicking on any of the buttons.
ERROR:
You will notice that the values in the input box does not updates to model value.
From what I can understand the issue is with my $render function.. however I can't seem to find a fix for it.
scope.handlePaste = function(e) {
var pastedText = e.clipboardData.getData('text/plain');
ngModelController.$setViewValue(pastedText);
ngModelController.$render = function() {
element.html($sce.getTrustedHtml(ngModelController.$viewValue));
};
return false; //prevent the default handler from running
};
}
You're using ngSanitize but you forgot to include it in the plunker. (lib + injection in app and $sce in directive).
Then element is an angular element and doesn't have an html() function.
You can get the raw html element with element[0] which has a property value.
http://plnkr.co/edit/HSOnscaprbyvUwjr0P2l?p=preview

Run code when link is clicked, even if I stay on the same page

I have a collapsible sidebar navigation, and any time users click a link on it, I have the sidebar hide automatically. I achieve this in the run method by using the following code:
app.run(function($rootScope) {
$rootScope.$on('$routeChangeSuccess', function () {
$rootScope.closeNavigation();
})
});
Where app is my Angularjs module.
The problem is that it gets a little unintuitive to use, when you click a link in the navigation for the page you're already on, and then nothing happens. What I would like is to have the sidebar close anyway, so that the users still get focus on the content, even if it's the same content.
But Angularjs doesn't execute the $routeChangeSucess event, if there is no route change happening. So what can I use instead?
You simply need to use ng-show = isPanelVisible and ng-click = closePanel() on the html sidepanel tag (e.g. a <div>).
Then, define $scope.closePanel() in the controller associated to the actual view. For instance
$scope.closePanel = function() {
$scope.isPanelVisible = false;
}
See an example here. Code is here. Note that it doesn't use ng-show, but the behaviour is the same.

Watch for dom element in AngularJS

I'm looking for a pure angularJS way to call a controller method once a particular dom element is rendered. I'm implementing the scenario of a back button tap, so I need to scroll to a particular element once it is rendered. I'm using http://mobileangularui.com/docs/#scrollable.
Update: how my controller looks like:
$scope.item_ready=function(){
return document.getElementById($scope.item_dom_id).length;
};
$scope.$watch('item_ready', function(new_value, old_value, scope){
//run once on page load, and angular.element() is empty as the element is not yet rendered
});
Thanks
One hack that you could do and I emphasize hack here but sometimes it's just what you need is watch the DOM for changes and execute a function when the DOM hasn't changed for 500ms which is accepted as a fair value to say that the DOM has loaded. A code for this would look like the following:
// HACK: run this when the dom hasn't changed for 500ms logic
var broadcast = function () {};
if (document.addEventListener) {
document.addEventListener("DOMSubtreeModified", function (e) {
//If less than 500 milliseconds have passed, the previous broadcast will be cleared.
clearTimeout(broadcast)
broadcast = $window.setTimeout(function () {
//This will only fire after 500 ms have passed with no changes
// run your code here
}, 10)
});
}
Read this post Calling a function when ng-repeat has finished
But don't look at the accepted answer, use the 3rd answer down by #Josep by using a filter to iterate through all your repeat items and call the function once the $last property returns true.
However instead of using $emit, run your function...This way you don't have to rely on $watch. Have used it and works like a charm...

Google maps not always fully rendering in Ionic

Having trouble with always rendering google maps in my Ionic app. When I first land on a view from a list of items on the previous view, the map always renders in its complete state. However, if I go back to the previous view and tap a different business, or even the same one, it appears as if the map is only rendering 25% of the complete map. I'm having this issue on both the emulator and on my iPhone.
Example
Code
getData.getBusinesses()
.then(function(data) {
// get businesses data from getData factory
})
.then(function(data) {
// get businesses photo from getData factory
})
.then(function(data) {
// get some other business stuff
})
.then(function() {
// get reviews for current business from separate async call in reviews factory
})
.then(function() {
// instantiate our map
var map = new GoogleMap($scope.business.name, $scope.business.addr1, $scope.business.city, $scope.business.state, $scope.business.zip, $scope.business.lat, $scope.business.long);
map.initialize();
})
.then(function() {
// okay, hide loading icon and show view now
},
function(err) {
// log an error if something goes wrong
});
What doesn't make sense to me is that I'm using this exact code for a website equivalent of the app, yet the maps fully load in the browser every time. The maps also fully load when I do an ionic serve and test the app in Chrome. I did also try returning the map and initializing it in a following promise, but to no avail.
I've also tried using angular google maps, but the same issue is occurring. I think I might want to refactor my gmaps.js (where I'm creating the Google Maps function) into a directive, but I don't know if that will actually fix anything (seeing as angular google maps had the same rendering issue).
I don't think the full code is necessary, but if you need to see more let me know.
EDIT
It seems that wrapping my map call in a setTimeout for 100ms always renders the map now. So I guess the new question is, what's the angular way of doing this?
I'm seeing similar issues with ng-map in Ionic. I have a map inside of a tab view and upon switching tabs away from the map view and back again, I would often see the poorly rendered and greyed out map as you describe above. Two things that I did that may help fix your issue:
Try using $state.go('yourStateHere', {}, {reload: true}); to get back to your view. The reload: true seemed to help re-render the map properly when the map was within the tab's template.
After wrapping the map in my own directive, I found the same thing happening again and wasn't able to fix it with the first suggestion. To fix it this time, I started with #Fernando's suggestion and added his suggested $ionicView.enter event to my directive's controller. When that didn't work, I instead added a simple ng-if="vm.displayMap" directive to the <ng-map> directive and added the following code to add it to the DOM on controller activation and remove it from the DOM right before leaving the view.
function controller($scope) {
vm.displayMap = true;
$scope.$on('$ionicView.beforeLeave', function(){
vm.displayMap = false;
});
}
Hope that helps.
don't use setTimeout on this!
You need to understand that the map is conflicting with the container size or something (example: map is loading while ionic animation is running, like swiping).
Once you understand this, you need to set map after view is completely rendered.
Try this on your controller:
$scope.$on('$ionicView.enter', function(){
var map = new GoogleMap($scope.business.name,
$scope.business.addr1, $scope.business.city,
$scope.business.state, $scope.business.zip,
$scope.business.lat, $scope.business.long);
map.initialize();
});

Resources