I am using ui-router's autoScroll to scroll down to a div (ui-view) when page loads/route change. It currently does so. Is there any way to offset autoScroll? I need to leave 100px above the element visible for menu and am confused on how to acchomplish this.
app.directive('scrollTop', function($uiViewScroll) {
var linker = function (scope, element, attr, $elm) {
$uiViewScroll(element);
};
return {
restrict: 'A',
link: linker
}
});
HTML:
<ui-view [autoscroll="true"]/>
autoscroll directive allows you to set the scroll behavior of the browser window when a view is populated.
By default, $anchorScroll is overridden by ui-router's custom scroll service, $uiViewScroll. This custom service let's you scroll ui-view elements into view when they are populated during a state activation.
Try to decorate the default $uiViewScroll service, overriding the default behaviour.
app.config(function ($provide) {
$provide.decorator('$uiViewScroll', function ($delegate) {
return function (uiViewElement) {
// var top = uiViewElement.getBoundingClientRect().top;
// window.scrollTo(0, (top - 30));
// Or some other custom behaviour...
};
});
});
See http://corpus.hubwiz.com/2/angularjs/22290570.html
Related
I am working on chat app, and the problem seems to be that directive for scroll bottom, to the last item in ng-repeat is not working properly when there are images in messages.
So it just doesn't scroll all the way bottom.
I think the reason is that directive scrolls before the image is fully loaded.
// Scroll to bottom directive
.directive('schrollBottom', function ($timeout) {
return {
scope: {
schrollBottom: "="
},
link: function (scope, element) {
scope.$watchCollection('schrollBottom', function (newValue) {
if (newValue)
{
$timeout(function() {
$(element[0]).scrollTop($(element)[0].scrollHeight);
}, 0, false);
}
});
}
}
})
Check here to see how to wait for an image to load before doing something. You can add a class to all images and wait for all images with that class to load before scrolling.
I would keep what logic you have, and put it inside of an img.onload callback handler. Then any scrolling to the bottom will only occur once all images have loaded.
I'm trying to implement the "infinite-scroll" feature in a list using a directive, which should load progressively a new set of "orders" when the scroll of the html element reaches or exceeds 75% of the scrollable height and append it to the existing list.
Unfortunately, the watcher doesn't trigger when i scroll the list.
The directive is located in the right tag and the watcher triggers the listener function only the first time, when the element is rendered by the browser.
The strange thing is that if i change path and then i return to the path where the list is, the watcher start behaving correctly and trigger the listener function everytime i perform a scroll.
<ol orders-loader class="orders-list">...</ol>
angular:
(function () {
angular.
module('myApp')
.directive('ordersLoader', ['$window', '$timeout', 'ordersResource', ordersloaderDirective])
function ordersloaderDirective($window, $timeout, loading, ordersResource) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
scope.orders = ordersResource; /*ordersResource use $resource to api calls
and then stocks the data in a array exposed in the scope*/
$timeout(function () {
scope.$watch(function () { return element[0].scrollTop }, function () {
if (*the scroll exceedes more or less 75% of the total scrollHeight*/) {
/*asking for more orders*/
}
});
}, 0);
}
}
}
I can't figure out where is the problem.
Solved
As yeouuu suggested, there was no digest cycle after the list scroll event, so i added:
element.bind('scroll', function () {
scope.$apply();
});
just before the $timeout function.
Whenever using plugins outside of angularJs that should trigger watcher you need to explicitly apply them. Otherwise Angular won't be aware of these changes/events.
In your case that means adding scope.$apply(); after the event.
Your edited solution:
element.bind('scroll', function () {
scope.$apply();
});
More information can be found here about the scope life cycle: https://docs.angularjs.org/guide/scope#scope-life-cycle
could you please tell me how to get event when user scroll to top .Actually I am using ng-repeat in my example .I want to get event when user scroll to bottom and scroll to top .I have one div in which I used ng-repeat can we get event of top when user move to top after scrolling.Actually I need to show alert when user scroll to bottom and top of div in angular .here is my code
<body ng-controller="MyController">
<div style="width:90%;height:150px;border:1px solid red;overflow:auto">
<div ng-repeat="n in name">{{n.name}}</div>
</div>
You could put directives on your scrollable div that listen to the scroll event and check for the top or bottom being reached.
So, using your HTML, your div would look like this:
<div exec-on-scroll-to-top="ctrl.handleScrollToTop"
exec-on-scroll-to-bottom="ctrl.handleScrollToBottom"
style="width:90%;height:150px;border:1px solid red;overflow:auto">
<div ng-repeat="n in name">{{n.name}}</div>
</div>
With new directives exec-on-scroll-to-top and exec-on-scroll-to-bottom added. Each specifies a function in your controller that should execute when the particular event the directive is checking for occurs.
exec-on-scroll-to-top would look like this, just checking for the scrollable div's scrollTop property to be 0:
myapp.directive('execOnScrollToTop', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var fn = scope.$eval(attrs.execOnScrollToTop);
element.on('scroll', function (e) {
if (!e.target.scrollTop) {
console.log("scrolled to top...");
scope.$apply(fn);
}
});
}
};
});
And exec-on-scroll-to-bottom would look like this (keeping in mind that an element is fully scrolled when its (scrollHeight - scrollTop) === clientHeight):
myapp.directive('execOnScrollToBottom', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var fn = scope.$eval(attrs.execOnScrollToBottom),
clientHeight = element[0].clientHeight;
element.on('scroll', function (e) {
var el = e.target;
if ((el.scrollHeight - el.scrollTop) === clientHeight) { // fully scrolled
console.log("scrolled to bottom...");
scope.$apply(fn);
}
});
}
};
});
Here's a plunk. Open the console to see messages getting logged when the scrolling reaches the top or bottom.
This is a non angular way, but you can wrap it up in a directive which also allows reuse:
Use Javascript event listener:
div.addEventListener('scroll', function(){
if(this.scrollTop===0)
//do your stuff
});
Make sure to use $apply if you make any changes to the scope variables inside this listener.
Related to this question but I don't want to create an attribute in the markup to call the function.
I have a sticky problem involving a controller, a directive with isolated scope and a disappearing kendo ui modal dialog. The culprit is of course IE.
When my app loads, it has a menu with an item called 'Open Dialog'. When you click it, it calls a function on a controller called callModal():
// javascript object used to build menu. Used by another directive to build the app's menu & is shown here just for illustration
"quickLinks": [ {"label": "Open Dialog", "action": "callModal()", "actionController": "ModalController"} ]
// the controller
angular.module('myModule').controller('ModalController', ['$scope','modalService', function ($scope, modalService) {
$scope.callModal = function () {
var modal = {
scope: $scope,
title: 'Add a user',
templateUrl: 'modal.html'
};
modalService.openDialog(modal, function (result) {
//service code to open a kendo ui dialog, not relevant to this question
});
};
}]);
When I reduce the browser (IE only, Chrome is fine), the modal dialog disappears from view. I can't even find it on the DOM in IE and I don't know why.
My proposed solution is to hook into the brower's resize event and recreate the dialog by calling the controller function, $scope.callModal().
To do this I have to use scope.$parent.$parent.callModal() and I've been told that this is wrong.
Can anyone suggest a better way of doing this?
This is my directive code:
angular.module('myModule').directive('modalDialog', ['$window', function (myWindow) {
return {
restrict: 'ACE',
replace: true,
transclude: true,
scope: {
// various attributes here
},
template: '<div kendo-window></div>',
compile: function (tElement, tAttrs, transclude) {
return function (scope, element, attrs) {
var viewWindow = angular.element(myWindow); // cast window to angular element
viewWindow.bind('resize', autoResizeHandler); // bind resize
var resizeHandler = function() { // the resize handler function
/*
Try find the dialog. If not found, then call
controller function to recreate
*/
var kendoModal = jQuery('div[kendo-window]');
if (kendoModal.length === 0) {
scope.$parent.$parent.callModal(); // this works but it's ugly!
}
};
I'm building an AngularJS app and would like some pointers on the way I'm structuring my Angular code.
For this module, I'd like to be able to click a button that will add a Div to a specific "parent div" that has JQuery UI draggable and resizable interactions attached to it.
They could add more than one Div that is draggable and resizable.
Right now, I have one directive that generates the whole Div. The event callback for when resizing is stopped is placed in the Button that has the directive attached to it.
Directive code gist:
var app = angular.module('myApp', []);
app.controller('IpadCtrl', function($scope) {
$scope.someFunc = function () {
console.log('resized');
alert('resized');
};
$scope.test = 'sup';
});
app.directive('addImageCont', function () {
return {
restrict: 'A',
link: function (scope, element, attributes) {
console.log(attributes);
element.on("click", function() {
var div = $("<div />").css({
'width': '100px',
'height': '100px',
'background-color': 'purple'
});
div.draggable();
div.resizable();
div.on('resizestop', function(event, ui) {
console.log(ui);
var cBack = scope[attributes.callback];
cBack();
});
element.parent().find('.ipad').append(div);
})
}
};
});
JSFiddle that has an example:
http://jsfiddle.net/GA2M8/3/
Is this the right way to handle callback events? I'd like to be able to have a callback for when resizing begins too.
I know there's a better way to handle this.
It has been suggested to me that I break up my directives.
One directive that just creates the Div and appends it to the Dom.
Another directive that attaches the JQuery UI draggable interaction to it.
Another directive that attaches the JQuery UI resizable interaction to it.
I'm not sure how to chain directives if that's how I should proceed.