I am attempting to create an angularjs bootstrap accordion that scrolls the accordion to the top when opened.
These solutions are close to what I would like to do:
However, they use a timeout or scope watches. I would like to avoid using these unless absolutely necessary.
Is there a way to accomplish this without using $watch
or setTimeout
?
Here is a plunk of what i am trying to do, this is using the $watch: https://plnkr.co/edit/XQpUdrdjqaCGom4L9yIJ
app.directive( 'scrollTop', scrollTop );
function scrollTop() {
return {
restrict: 'A',
link: link
};
}
function link( scope, element ) {
scope.collapsing = false;
var jqElement = $( element) ;
scope.$watch( function() {
return jqElement.find( '.panel-collapse' ).hasClass( 'collapsing' );
}, function( status ) {
if ( scope.collapsing && !status ) {
if ( jqElement.hasClass( 'panel-open' ) ) {
$( 'html,body' ).animate({
scrollTop: jqElement.offset().top - 30
}, 500 );
}
}
scope.collapsing = status;
} );
}
I have found a way to do this from the controller.
I added a function that is triggered on ng-click
to report the is-open
status of the accordion.
Using the component lifecycle hook $doCheck
I was able to watch for changes to the state of vm.isOpen
. $doCheck
runs on the end of every digest cycle, so I did not need to set a $scope.watch
or $timeOut
The $doCheck
runs essentially the same code as the directive in the question
app.controller('homeController', function($state, $element, sections, $transitions) {
var vm = this;
vm.$onInit = function() {
vm.sections = sections.getSections();
};
function updateOpenStatus() {
vm.collapsing = false;
vm.isOpen = vm.sections.some(function(item) {
return item.isOpen;
});
}
vm.$doCheck = function() {
if (vm.isOpen) {
var elem = $element.find('.panel-collapse');
var status = elem.hasClass('collapsing');
if (vm.collapsing && !status) {
var parentElem = elem.closest('.panel-open');
if (elem.parent().hasClass('panel-open')) {
$('html,body')
.stop()
.animate({
scrollTop: parentElem.offset().top - 52
}, 'fast');
}
}
vm.collapsing = status;
}
};
});
I updated the uib-accordion
to call the function in the controller
<uib-accordion>
<div heading="Section Title" is-open="section.isOpen" ng-repeat="section in vm.sections" scroll-top uib-accordion-group>
<uib-accordion-heading>
<div ng-class="{isOpen: section.isOpen}" ng-click="vm.toggleOpen()">
<h3>{{section.sectionTitle}}</h3>
</div>
</uib-accordion-heading>
<div class="clearfix">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</div>
</div>
</uib-accordion>
Updated Plnkr: https://plnkr.co/edit/5EqDfmVOa0hzFfaQqdI0?p=preview