Changing css on scrolling Angular Style - angularjs

I want to change CSS elements while a user scrolls the angular way.
here's the code working the JQuery way
$(window).scroll(function() {
if ($(window).scrollTop() > 20 && $(window).scrollTop() < 600) {
$('header, h1, a, div, span, ul, li, nav').css('height','-=10px');
} else if ($(window).scrollTop() < 80) {
$('header, h1, a, div, span, ul, li, nav').css('height','100px');
}
I tried doing the Angular way with the following code, but the $scope.scroll seemed to be unable to properly pickup the scroll data.
forestboneApp.controller('MainCtrl', function($scope, $document) {
$scope.scroll = $($document).scroll();
$scope.$watch('scroll', function (newValue) {
console.log(newValue);
});
});

Remember, in Angular, DOM access should happen from within directives. Here's a simple directive that sets a variable based on the scrollTop of the window.
app.directive('scrollPosition', function($window) {
return {
scope: {
scroll: '=scrollPosition'
},
link: function(scope, element, attrs) {
var windowEl = angular.element($window);
var handler = function() {
scope.scroll = windowEl.scrollTop();
}
windowEl.on('scroll', scope.$apply.bind(scope, handler));
handler();
}
};
});
It's not apparent to me exactly what end result you're looking for, so here's a simple demo app that sets the height of an element to 1px if the window is scrolled down more than 50 pixels: http://jsfiddle.net/BinaryMuse/Z4VqP/

Related

angularjs set background-image

I have a div to visualize progress.
Therefor I have this ng-style definition:
ng-style="{'background-image':'linear-gradient(left, rgba(255,0,0,1)'+bufferProgressPercent!=='NaN'?bufferProgressPercent:0+'%,rgba(0,0,0,0)'+(bufferProgressPercent!=='NaN'?bufferProgressPercent:0)+'%)!important'};"
//output in developer-tools
<div class="audio-controls-wrapper" ng-style="{'backgroundImage':'linear- gradient(left, rgba(255,0,0,1)'+bufferProgressPercent!=='NaN'? bufferProgressPercent:0+'%,rgba(0,0,0,0)'+(bufferProgressPercent!=='NaN'? bufferProgressPercent:0)+'%)'};">
The document only shows the as clear-text. The values don't get rendered.
The values are correct:
{{'background-image:linear-gradient(left, rgba(255,0,0,1)'+ (bufferProgressPercent!=='NaN'?bufferProgressPercent:0)+'%,rgba(0,0,0,0)'+(bufferProgressPercent!=='NaN'?bufferProgressPercent:0)+'%)'}}
Gives this out:
background-image:linear-gradient(left, rgba(255,0,0,1)59%,rgba(0,0,0,0)59%)
Another attempt was to create a directive:
<div class="audio-controls-wrapper" progress-animation="bufferProgressPercent" >
Directive:
scope.$watch('progressAnimation', function(current, old){
if(angular.isDefined(current) && current !== old){
var backgroundImage = 'linear-gradient(left, rgba(255,0,0,1)'+ (current!=='NaN'?current:0)+'%,rgba(0,0,0,0)'+(current!=='NaN'? current:0)+'%)!important';
//scope.$applyAsync(function(){
//element.css({'backgroundImage':backgroundImage});
element[0].style.backgroundImage = backgroundImage;
$compile(element.contents())(scope);
//});
console.log(backgroundImage)
console.log(element[0].style)
}
});
But the attribute backgroundImage of this element is never set.
Did you enter the watch function into directive ?
for example
.directive('progressAnimation', function () {
return {
restrict: 'A', //E = element, A = attribute, C = class, M = comment
scope: { // input the var that you want to watch
},
link: function ($scope, element, attrs) {
//put your watch function here
if(angular.isDefined(current) && current !== old){
var backgroundImage = 'linear-gradient(left, rgba(255,0,0,1)'+ (current!=='NaN'?current:0)+'%,rgba(0,0,0,0)'+(current!=='NaN'? current:0)+'%)!important';
element[0].style.backgroundImage = backgroundImage;
$compile(element.contents())(scope);
console.log(backgroundImage)
console.log(element[0].style)
}
} //DOM manipulation
}
});
To set the background in I had to use
background
instead of
background-image
further I had to replace
linear-gradient(...
with
-moz-linear-gradient(
that means
{{'background-image:linear-gradient(left, rgba(255,0,0,1)'+ (bufferProgressPercent!=='NaN'?bufferProgressPercent:0)+'%,rgba(0,0,0,0)'+(bufferProgressPercent!=='NaN'?bufferProgressPercent:0)+'%)'}}
became this:
{{'background:-moz-linear-gradient((left, rgba(255,0,0,1)'+ (bufferProgressPercent!=='NaN'?bufferProgressPercent:0)+'%,rgba(0,0,0,0)'+(bufferProgressPercent!=='NaN'?bufferProgressPercent:0)+'%)'}}
I guess I have to add all the browser besides chrom a certain linear gradient.

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"));

Floating title is not working in 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/

Angular Animation: How to add extra classes to a specific element when animation starts and ends?

I'm using state router to transition between pages.
I need to add a class to the <body> while the animation is running and remove it once the enter and leave animations are completed.
I tried to create a directive an inject the $animate service.
Then I started listening for enter and leave events as suggest in documentation.
The html:
<div class="ui-view-container">
<div ui-view style="height:100%;" class="suffle-page" suffle-page></div>
</div>
The directive:
;(function(){
angular.module('app')
.directive('sufflePage',function($animate){
var $body = $('body');
return {
link: function (scope, element) {
//var $el = $('[ui-view]');
$animate.enter(element,
function callback(element, phase) {
//$body.addClass('animating');
}
);
$animate.leave( element, function(){
function callback(element, phase) {
//$body.removeClass('animating')
}
})
}
}
});
})();
Then I have the CSS that animates those views
//prevents animation in mobile devices to faster performance
.ui-view-container {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
overflow: hidden;
}
[ui-view].ng-enter, [ui-view].ng-leave {
...
}
[ui-view].ng-enter {
..
}
[ui-view].ng-enter-active {
..
}
[ui-view].ng-leave {
...
}
[ui-view].ng-leave-active {
...
}
body.animating{
/*this element is outter of the animation that's why i must append a class to the top level element. in this case body*/
.special-element{
display: none;
}
}
At $animate.enter(element...) an error is thrown:
TypeError: Cannot read property 'createDocumentFragment' of null
Any help?
I was misunderstanding the use of $animate.enter and $animate.leave and **I also did use an incorrect version of angular because the $animate.leave are part of 1.4.x versions an my project was built on top of version 1.3.0.
After updating the angular.js and angular-animate.js all i had to do was
1) create the directive that will monitor enter:start and enter:end events
2) load the directive into the project
3) and write the piece of code that adds the class to the body during the animation.
I hope it helps.
.directive('sufflePage',function($animate){
var $body = $('body');
return {
link: function (scope, element) {
if (!element){
return;
}
/***
* when the ui-view that is `entering` the page stars it adds the animating class to body
* when it leaves it removes the animating from the body class
*
* IMPORTANT: this works because the enter and exit animation are triggered in parallel with the same duration
*
*/
$animate.on('enter', element,
function callback(element, phase) {
if (phase == 'start'){
$body.addClass('animating');
} else {
$body.removeClass('animating');
}
}
);
scope.$on('$destroy', function(){
$animate.off('enter',element);
});
}
}

multiple right click contextmenu angularjs

i m trying to implement a dropdown on right click using this directive contextMenu in angularjs. This directive works fine in firefox but doesnt close the old menu while opening a different one when using ' google chrome'. Any idea how can i make changes to it.
Here is the plunkr
(function(angular) {
var ngContextMenu = angular.module('directive.contextMenu', []);
ngContextMenu.directive('cellHighlight', function() {
return {
restrict: 'C',
link: function postLink(scope, iElement, iAttrs) {
iElement.find('td')
.mouseover(function() {
$(this).parent('tr').css('opacity', '0.7');
}).mouseout(function() {
$(this).parent('tr').css('opacity', '1.0');
});
}
};
});
ngContextMenu.directive('context', [
function() {
return {
restrict: 'A',
scope: '#&',
compile: function compile(tElement, tAttrs, transclude) {
return {
post: function postLink(scope, iElement, iAttrs, controller) {
var ul = $('#' + iAttrs.context),
last = null;
ul.css({
'display': 'none'
});
$(iElement).bind('contextmenu', function(event) {
event.preventDefault();
ul.css({
position: "fixed",
display: "block",
left: event.clientX + 'px',
top: event.clientY + 'px'
});
last = event.timeStamp;
});
//$(iElement).click(function(event) {
// ul.css({
// position: "fixed",
// display: "block",
// left: event.clientX + 'px',
// top: event.clientY + 'px'
// });
// last = event.timeStamp;
//});
$(document).click(function(event) {
var target = $(event.target);
if (!target.is(".popover") && !target.parents().is(".popover")) {
if (last === event.timeStamp)
return;
ul.css({
'display': 'none'
});
}
});
}
};
}
};
}
]);
})(window.angular);
Change the .click event to .mouseup event and it will work with chrome.
$(document).mouseup(function(event) {
var target = $(event.target);
if (!target.is(".popover") && !target.parents().is(".popover")) {
if (last === event.timeStamp)
return;
ul.css({
'display': 'none'
});
}
});
I faced the same problem, and it worked for me. :)
Looking at the source code (of the directive), I think this context menu directive is a little bit too simple. It simply doesn't do a whole lot more than triggering a show/hide on the element referenced by the context attribute. It may have been enough for the use case of the one who wrote it, but it appears to be too lightweight for a general solution.
What is happening in the directive code: If you happened to trigger a context menu on the same row (or more general reference the same context menu) it works correctly because it will simply show the current context menu at a different place. If you trigger context1 first and then (by clicking on the second row) trigger a different context menu context2 there simply isn't any code that would trigger a hide of the context1 context menu.
You could implement this yourself as well but then keep track of any already opened context menu's and close them before another one is opened.
Btw: this context menu doesn't work for me in Firefox (38, Mac OS X) either. It opens the context menu and immediately closes it again. This is probably because both the contextmenu (on the table row) and the click (on document) are triggered.

Resources