Floating title is not working in Angularjs - angularjs

I have a list of item with two iterations. I want a sticky title when the title scroll up from the view area. I have done it with jquery, but can't able to do in angular. Created a fiddle https://jsfiddle.net/1vf5ska7/
I just to want to add a class in tag when the title is goes up to the view area.
angular.element(document.querySelector('#l-content__desc__split1__body')).on('scroll', function() {
});
And the important thing is it is not a window scroll. It's a div scroll
Please help me.
Thanks..

You need to include a directive and operate on it. If $window.pageYOffset is greater than the position of the element you apply a specific class to that element which is positioned fixed.
var app = angular.module('app', []);
app.directive('setClassOnTop', function ($window) {
var $win = angular.element($window); // wrap window object as jQuery object
return {
restrict: 'A',
link: function (scope, element, attrs) {
var title = angular.element(document.getElementById('sticky-title'));
var offsetTop = title[0].offsetTop;
$win.on('scroll', function (e) {
if ($window.pageYOffset > offsetTop) {
angular.element(title[0]).addClass('floating-title');
} else {
angular.element(title[0]).removeClass('floating-title');
}
});
}
};
});
And here is the updated fiddle:
https://jsfiddle.net/1vf5ska7/3/

Related

addClass() doesn't work with getElementById in AngularJS

I'm trying to manipulate an element class within a directive. The directive is of a toolbar and it's supposed to add a class to 2 elements after some scroll.
The element directive itseld;
The view, to add/remove margin;
This is my html structure:
<ag-toolbar class="ag-toolbar--sec"></ag-toolbar>
<div ui-view="app" autoscroll="false" id="appView"></div>
And this is my directive:
function agToolbar($window) {
return {
restrict: 'E',
link: function(scope, element, attrs) {
var elView;
setTimeout(function(){
elView = document.getElementById("appView");
}, 400);
angular.element($window).bind("scroll", function() {
if (this.pageYOffset >= 128) {
element.addClass('scroll');
elView.addClass('agMargin');
} else {
element.removeClass('scroll');
elView.removeClass('agMargin');
};
});
}
};
}
In the console I keep getting the error:
elView.addClass is not a function
elView.removeClass is not a function
But the element.addClass is working fine. Any ideas why?
addClass belongs to jqLite (or jQuery if available), see https://docs.angularjs.org/api/ng/function/angular.element.
That is, you need to wrap the DOM element in a jqLite/jQuery element:
elView = angular.element(document.getElementById("appView"));

Angular - push back to array after splice

I'm using next directive to splice array if image src error.
myApp.directive("noImage", function () {
return {
link: function (scope, element, attrs) {
element.bind("error", function () {
var idx = scope.posts.indexOf(scope.post);
scope.$apply(function () {
scope.posts.splice(idx, 1);
});
});
}
};
});
And trying to push spliced items back with function from controller something like.
$scope.restorePosts = (function() {
return function() {
$scope.spliced_posts.forEach(function(x) {
return $scope.posts.push(x);
});
return $scope.spliced_posts = [];
};
});
The question is what i should add to directive?
scope.spliced_posts.push(scope.post)?
JSFIDDLE
If I'm correct, you want to only show the images that have no "error.jpg" in them.
Removing them and later adding them to your array might not be the best solution here. You can use a filter with your ng-repeat to filter out the images with error.jpg in it.
ng-repeat="post in posts | filter:{ image_url:'!http://example.com/error.jpg'}"
If anywhere else you want to add a class to images that DO have the error in it, you can use ng-class:
ng-class="{'no-img': post.image_url=='http://example.com/error.jpg'}" ng-src="{{post.image_url}}"
http://jsfiddle.net/jkrielaars/xxL0qyy6/3/
That way your array of posts remains intact. If you want to be able to toggle between showing the posts with and without error.jpg, you could toggle the filter instead of changing your posts array.
in your controller
$scope.myFilter = null;
$scope.toggleFilter = function(){
if( $scope.myFilter ){
$scope.myFilter = null;
}else{
$scope.myFilter = { image_url:'!http://example.com/error.jpg' };
}
}
and in the view:
<li ng-repeat="post in posts | filter:myFilter">
UPDATE
If you want to do dynamic checking to see if the image triggers an error, you can use a directive, much like the one you originally used, to add a class, or completely hide the element. I have also updated the JSfiddle. Something like this would add an error class to the parent. You could change this to whatever you like to style your parent. You could also choose to just hide the parent straight from the directive.
myApp.directive("hideNoImage", function () {
return {
link: function (scope, element, attrs) {
element.bind("error", function () {
element.parent().addClass("error");
});
}
};
});
Use angular.copy() to make a copy of the original array that you receive from your api and store in your service

angularjs have a directive only apply for select controllers

I'm using a directive to implement scrolling on a page from an a to a div. Meaning that by clicking on an a looks like:
<a data-scroll-on-click="" href="#projects">Projects</a>
The page smoothly scrolls to:
<div id="projects">
To make this happen, I am using an attribute scroll-on-click via the following directive:
consortiumApp.directive('scrollOnClick', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var idToScroll = attrs.href;
element.on('click', function(event) {
event.preventDefault();
var $target;
if (idToScroll) {
$target = $(idToScroll);
} else {
$target = element;
}
$("body").animate({scrollTop: $target.offset().top}, 1500, 'easeInOut
});
}
}
});
The preventDefault stops the router from kicking in.
The a with the data-scroll-on-click attribute is part of a navbar that I would like to include on other pages via ng-include. However, this means that the a element will have the scroll-on-click attribute on pages where scrolling does not make sense. Meaning that when the navbar is on other pages besides the main page, I want the anchors in it to function like links back to the main page and not to trigger scrolling.
I'm not sure what a good solution is: I'm not sure if it is possible to have scroll-on-click appear only when there is a certain active controller (a sort of conditional attribute)? Or if it is possible to indicate the current active controller in a directive?
Worst comes to worst, I will just write two navbars - one for the main page that implements scrolling via a directive and one for subsidiary pages that implements linking via the router, but I have a feeling there is a more concise way to do this.
I think you can pass boolean value along with the data-scroll-on-click='true/false' and check this in the directive if its true than proceed for scroll otherwise ignore it.
<a data-scroll-on-click="" href="#projects">Projects</a>
And
consortiumApp.directive('scrollOnClick', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var idToScroll = attrs.href;
var scroll_needed = attrs.scrollOnClick;
if(scroll_needed === true){
element.on('click', function(event) {
event.preventDefault();
var $target;
if (idToScroll) {
$target = $(idToScroll);
} else {
$target = element;
}
$("body").animate({scrollTop: $target.offset().top}, 1500, 'easeInOut
});
}
}
}
});

Angular navigate to a specific tab on a page

I would like to navigate to a page of my single page application using a link like /#/invoices/1?tab=2. This page contains tabs, so I would also like to send the user directly to the tab I wish through the url.
Could you provide some pointers on how to implement this in the cleanest way?
Use $anchorScroll method, passing a class name on it you can go directly to that element.
Just an update to above answer.
Few examples with custom directives
http://nadeemkhedr.com/angularjs-scroll-to-element-using-directives/
angular.module('MyApp').directive('scrollToBookmark', function() {
return {
link: function(scope, element, attrs) {
var value = attrs.scrollToBookmark;
element.click(function() {
scope.$apply(function() {
var selector = "[scroll-bookmark='"+ value +"']";
var element = $(selector);
if(element.length) {
window.scrollTo(0, element[0].offsetTop – 100);
// Don’t want the top to be the exact element, -100 will go to the top for a little bit more
}
});
});
}
};
});

When do we know the actual width of an element in Angular?

I'm tring to create a directive that will center a div.
So far, I have this code:
app.directive("setcenter", function () {
return {
scope:{
setcenter: '='
},
link: function (scope, element, attrs) {
scope.$watch('setcenter', function (newValue) {
if (newValue == true) {
var width = element.width();
element.css('position', 'absolute');
element.css('top', '80px');
element.css('left', '50%');
element.css('z-index', '200');
element.css('margin-left', '-' + width / 2 + 'px');
}
});
}
}
});
The problem is the width of the element. The whole point for this directive is that the div that uses this directive, don't have a width set. I want this directive to figure out the width and center the div.
The problem I encounter is that when the directive is invoked, the actual width of the div is not yet known. When I use this in my situation, the div is 800px, but when the page is finished loading, the div is 221px.
So, what can I do to wait till the actual width is known of the div?
First, I only have used this logic when I defined a controller for a directive rather than a link function. So defining it in a link function instead may cause different behavior, but I suggest you try it there first and if you can't get it to work then switch to using a controller.
As far as I can tell, the only change you would need to make this work would be to change $scope to scope calls and $element to element since the dependency injected objects become standard link function parameters.
$scope.getElementDimensions = function () {
return { 'h': $element.height(), 'w': $element.width() };
};
$scope.$watch($scope.getElementDimensions, function (newValue, oldValue) {
//<<perform your logic here using newValue.w and set your variables on the scope>>
}, true);
$element.bind('resize', function () {
$scope.$apply();
});
The idea for this usage came to me after reading a very similar type of usage about watching the $window rather than the current element, the original work can be found here.
Angular.js: set element height on page load
James' answer led me to:
app.directive('measureInto', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
scope.$watch(function() {
return element[0].clientWidth;
}, function(value){
scope[attrs.measureInto] = element[0].clientWidth + 10;
});
}
};
});
So, at runtime, I add this and assign into whatever scope variable I want the width of the element I'm looking for
I had a similar issue and found that the dimensions were reliably correct when all the ng-ifs (or anything else using ngAnimate) on the page had been resolved - it's possible something similar is happening here. If so, this would do the trick without adding any new listeners:
$scope.tryGetElementDimensions = function () {
if (!angular.element("your-element") || ((angular.element("your-element")[0].classList).contains("ng-animate")
$timeout(function() {
$scope.tryGetElementDimensions()
})
}
else {
$scope.getElementDimensions()
}
$scope.getElementDimensions = function (){
//whatever you actually wanted to do
}
link: function (scope, element, attrs) {
$scope.tryGetElementDimensions()
}
Angular adds ng-animate and ng-enter, ng-leave classes while it's animating and you can be confident it's finished when these classes have all been removed. $timeout without a second argument just waits for the next digest.
Can't comment yet, therefore this answer.
Found a similar solution like the one strom2357 is suggesting. $timeout works really well to let you know when the dom is ready, and it is super simple. I am using this solution to get the ui-view element width. Found it in a fiddle.
var app = angular.module('app', []);
app.controller('MyController', function($timeout, $scope){
$timeout(function(){
//This is where you would get width or height of an element
alert('DOM ready');
});
alert('DOM not ready');
});

Resources