I know how to detect the document end while scrolling in plain JS, my problem is - how to implement that in AngularJS, I know I should attach the scroll event to both the $window and $document, my question is about where to implement the behavior?, directives?, services?, if anyone can show me what is the right way to implement that kind of detection in AngularJS I'll be very thankful.
After struggling with it for a long time- stumbled across the library: http://binarymuse.github.io/ngInfiniteScroll/documentation.html.
Based on your use-case, you could do something like:
<div infinite-scroll="addMoreItems()">
<div ng-repeat="item in items">Item number {{$index}}: {{$item}}</div>
</div>
As it allows you to attach this to any div- you could almost do anything in the function you want to.
Depending on what you're doing it can go in a combination of places. Typically when dealing with DOM manipulation (which is what I assume will be happening) you should use a directive - something like:
app.directive("scrollDetector", ["$document", "$window", function($document, $window) {
return {
restrict: "A",
link: function(scope, elem, attrs) {
angular.element($window).bind("scroll", function() {
//scroll logic here
});
}
}
}]);
And then implement the scroll-detector directive. (ex: <div scroll-detector></div>)
Related
angular.module(module.name).directive(current.name, ['$timeout', function (timeout) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
element[0].style.margin = '0.1px';
timeout(function () {
element[0].style.margin = '0px';
}, 3000);
}
}
}]);
The above angular code is something that I wrote so when the browser loads it'll repaint the browser after to show the content on a single page app properly. However I have come to another issue where I need to do this on Resize.
I want to be able to use the same function below and add resize to it.. I'm a bit lost if I can add resize to this or will I need to write a different script.
I'm still coming to grips with angular so I'm looking for an answer that will explain this properly and what would be the best practise for what I want.
Do you want to apply it on any resize of the window or just below particular ratio/size?
If you want on any resize of the window, take a look at this JSFiddle
scope.$watch(scope.getWindowDimensions, function (newValue, oldValue) {...
In an Angular (1.3) app, I am displaying list of records using ng-repeat. There is a directive with a template inside the ng-repeat. Within the template I'm using ShareThis controls which are activated after the DOM is loaded.
On initial load of the app, the ShareThis Javascript works correctly and activates the buttons. On route change it does not activate. I've found to references to activate the controls manually via stButtons.makeButtons() or stButtons.locateElements();, but I'm unsure where to call this function in the directive or page cycle. I've tried within:
the directive link function - using $timeout or scope.$watch
the template <script>stButtons.locateElements();</script> - activates before model binding
the controller after binding - activates before DOM rendered
My understanding is the function to activate needs to be called after binding and after DOM rendering, but Angular does not know when the DOM is ready. There is a method to dynamically render the ShareThis controls using only Javascript, but I want the HTML defined in the template not Javascript for this case.
I've seen several questions out there related, but none of the answers seem to work 100% for my scenario (and many are broken as of Angular 1.3).
item-list.html (view)
<div ng-repeat="item in vm.itemList">
<item-directive item="item"></item-directive>
</div>
item-list.cs (controller)
{ ... vm.itemList = getItems(...) ... }
item-directive.js (directive)
(function () {
angular.module('app');
function itemDirective() {
var directive = { templateUrl: 'item.html', link: linkFunc, controller: ItemDirective };
return directive;
function linkFunc(scope, element, attr, ctrl) { var item = scope.item }
}
ItemDirective.$inject = ['$scope'];
function ItemDirective($scope) { ... }
}
item.html (directive template)
...
<div class="item-share-section">
<span class='st_sharethis_large' st_url="{{vm.item.url}}" st_title="{{vm.item.name}}"></span>
</div>
...
if I understood well, you want to call the function after the dom is completely render, right? Try this inside the postLink of your directive:
$scope.$watch('viewContentLoaded', stButtons.locateElements())
While my solution is a little hackish, I still prefer it over using $watch, since that is inefficient. Instead, I initialize the function which loads the buttons when the particular view you want to load the buttons with is rendered. The technique is as follows:
Here is the function which you should put in your controller:
$scope.loadShareThis = function() {
stButtons.makeButtons();
}
You'd then add to your item-list.html as such:
<div ng-repeat="item in vm.itemList" ng-init="loadShareThis()">
<item-directive item="item"></item-directive>
</div>
The dynamic URL's might give you additional problems, but that's another issue all together.
I have got a piece of code in an angular application, that writes a navigation tree into a <div>. This tree may change its size depending on the model of mymodule.tree.folders. Now I want to write a directive class="scrollable", that adds the functionality of jquery.nicescroll to the wrapping <div>:
<div class="scrollable">
... here is my resizing tree ...
</div>
Now, I have to call the resize() function of nicescroll each time, the tree model mymodule.tree.folders changes. This is necessary to make this library work as expected, after the content changes its size.
My question now is: How do I put the model mymodule.tree.folders (it is an array) into my directive to be able to $watch() it there? I would like to write something like this:
<div class="scrollable" scrollable-watch="mymodule.tree.folders">
... here is my resizing tree ...
</div>
...in my template. Is this possible, to fetch this model from the templates scope or do I have to serialize the whole tree into an extra variable?
I found a way to access the model through the $parent scope:
'use strict';
angular.module('shared.directives.scrollable', []);
angular.module('shared.directives.scrollable').directive('scrollable', function() {
return {
restrict: 'C',
link: function(scope, element, attrs) {
scope.$watch('$parent.' + attrs.scrollableWatch, function(newValue, oldValue) {
console.log('Model changed!');
}, true);
}
};
});
It seems a bit hackish to access the parent scope, but because this is allways the caller scope, I think it is okay.
When the user clicks a link, I want to execute some code in a function inside the controller. But I want to prevent that the URL changes.
I tried the following possibilities
Removed the href-attribute. Didn't work, still changes the url to '/'
Tried ng-click='deleteUser(user.id, $event)' and $event.preventDefault() in my deleteUser()-function. Didn't work.
What did work is a hack I've found on GitHub about an unintended reload.
This is how I do it now:
<a ng-click="deleteUser(user.id)" href="javascript:">Delete user</a>
Question
What is the'clean' method to prevent a link from changing the URL?
<a ng-click="deleteUser(user.id)" href="">Delete user</a>
If you look at the source code for the a element directive (which is a part of the Angular core), it states at line 29 - 31:
if (!element.attr(href)) {
event.preventDefault();
}
Which means Angular already is solving the issue of links without a href. The only issue you still have is the css problem. You can still apply the pointer style to anchors that have ng-clicks, e.g.:
a[ng-click] {
/* Styles for anchors without href but WITH ng-click */
cursor: pointer;
}
So, you could even make your site more accessible by marking real links with a subtle, different styling then links that perform functions.
Happy coding!
The real problem is in the a directive
That's right, every <a></a> element is actually an AngularJS directive.
It seems to fix some issues with IE if you look the comments in the code.
But everything for me is working great when I just removed the directive from the AngularJS core code.
I was having the same problem as you did and tried all of the other solutions. The difference is that I had the problem only in IE.
If you don't want to play with building the AngularJS script from source, just search for htmlAnchorDirective in the angular.js file and remove/comment it out.
I believe there is a bigger problem here which should be addressed in the AngularJS core, but I haven't found it yet.
UPDATE: This solution is most probably outdated now! You should try using the latest AngularJS version.
What exactly didn't work when you removed the href attribute?
That's exactly what you should do. See this fiddle for an example:
http://jsfiddle.net/terebentina/SXcQN/
As #Justus pointed out, if the source code is like:
if (!element.attr(href)) {
event.preventDefault();
}
Making element.attr(href) as null string '' should get you inside the if condition, as !'' evaluates to true. That is the solution by #umur
I have always been doing deleteUser($event,user.id) and it seemed to work. A possible problem would be the ordering of the variables to your click handler. The first argument should probably be the $event object.
I use my custom directive to accomplish this. Its mechanism is to operate ng-click directive itself.
angular.module('delete', [])
.directive('buttonDelete', ['$parse', function ($parse) {
var confirmFunc = function (scope, element, attr, event, ngClick) {
if (! confirm("Are you sure?")) {
event.preventDefault();
}
else {
// Copy from ngEventDirectives initialization.
var fn = $parse(ngClick);
scope.$apply(function() {
fn(scope, {$event:event});
});
}
};
return {
restrict: 'C',
compile: function (element, attr) {
var ngClick = attr.ngClick;
attr.ngClick = '';
return {
pre: function (scope, element, attr) {
element.click(function (e) {
confirmFunc(scope, element, attr, e, ngClick);
});
}
};
}
};
}]);
This is admittedly a hack, but I did:
<span class="link" ng-click="deleteUser(user.id)">Delete user</span>
and in my css had
span.link {
/* style like a link */
}
The following worked great for me:
<a ng-href="javascript: return false;" ng-click="alertSearchCtrl.deleteAlert()">Remove</a>
specifically, I'm AJAXing in some date ranges into a pair of SELECTs (maintained with an AngularJS ng-repeat), which I'm then turning into a slider using jQuery UI's selectToUISlider. I assume selectToUISlider's behavior is undefined if the SELECTs it's operating on are modified while it's running. is there a way to ensure that it is called only after AngularJS has finished updating? I haven't encountered any problems yet, but I'm not sure I'm just lucky, or haven't had my computer lag at just the right moment, etc...
I can think of two solutions, but neither seems very good:
don't use the ng-repeater; build the SELECTs with jQuery. but that'd be sad to have to do.
delay the call to selectToUISlider using setTimeout. but that seems... inelegant.
I don't know how selectToUISlider works, but you want a directive. In that directive, $watch for changes to the list and update the slider however it's supposed to be done.
http://docs.angularjs.org/api/ng.$rootScope.Scope#$watch
Example HTML:
<select jq-slider="myList" ng-options="item.val for item in myList"></select>
Example JS:
myApp.directive('jqSlider', function() {
return {
link: function(scope, elm, attrs) {
scope.$watch(attrs.jqSlider, function whenMyListChanges(newValue, oldValue) {
$(elm).applySelectUISliderUpdateForNewValues();
});
}
};
});