I'm trying to make a virtual scroll and whenever the user scrolls down I need to add a negative top equal to the container height to each row. But of course this top property can vary depending of some factors like the user's screen resolution or browser window size.
So far this is what I got:
<div class="container" id="my-container">
<!--If it has the class row-scrolled the top property is applied-->
<div ng-repeat="(row) in virtualCollection"
ng-class="{'row-scrolled': controller.isScrolled}">
<!-- row properties -->
</div>
</div>
I have also thought about the idea of using ng-style but would override any style from my .css file.
Is there anyway to get the size/property of a DOM element...
// controller
var containerHeight = angular.element('#my-container')[0].clientHeight;
var cssProperty = '-' + containerHeight + 'px';
And then use it in an css?
// css
.row-scrolled {
top: cssProperty;
}
You can't pass variables from javascript to CSS since CSS is not a programming language but a style sheet language.
What you can do is manipulating specific elements with javascript.
Based on your code here is an example:
// controller
var containerHeight = angular.element('#my-container')[0].clientHeight;
var cssProperty = '-' + containerHeight + 'px';
var $$rowScrolled = document.querySelectorAll(".row-scrolled");
if ($$rowScrolled && $$rowScrolled.length > 0) {
for (var i = 0; i < $$rowScrolled.length; i++) {
var $rowScrolled = $$rowScrolled[i];
$rowScrolled.style.top = cssProperty;
}
}
With jQuery:
// controller
var containerHeight = angular.element('#my-container')[0].clientHeight;
var cssProperty = '-' + containerHeight + 'px';
var $rowScrolled = $(".row-scrolled");
if ($rowScrolled && $rowScrolled.length > 0) {
$rowScrolled.css("top", cssProperty);
}
You can not pass a variable to the CSS.
What you can do though is add the property directly using the ng-style tag:
<div class="container" id="my-container">
<!--If it has the class row-scrolled the top property is applied-->
<div ng-repeat="(row) in virtualCollection"
ng-style="{'top': controller.cssProperty}">
<!-- row properties -->
</div>
Related
I have such an div element:
<div className="rate" >
<mark className="rate-mark"> Rate : </mark>
<mark className="val-mark" style={{color}}> {rate}</mark>
</div>
where the color is a hook:
var green = "#0cb87f";
var red = "#ce2020";
const [color,setColor] = useState(green)
I'm trying to dynamically change the color like this :
if (response.getRate() < rate){
setColor(red)
}else{
setColor(green)
}
But in my case it turns out that the light is always green.I think the mark page element is just never updated.How can this be made to work?
I'm calculating containers heights as per viewport by ng-style through my custom method.
Everything works well, but it keeps calling the method even if the element is styled. I have a big amount of DOM elements that are need to be styled. That's why, I can't allow continuous execution for all elements. Please note, I can't use ng-class because each element contains different content. And can't use controller scope variable due to unlimited numbers of elements.
HTML:
<div class="myElement" ng-style="styleElement('myElement')">
...
...
</div>
Function:
$scope.styleElement = function (elementCls) {
var elementToSet = $('.'+elementCls+':visible');
if(elementToSet.length){
var winHeight = $( window ).height();
var eTop = elementToSet.offset().top;
if(eTop == 0){
var elemChilds = elementToSet;
var elemChildsLen = elemChilds.length;
for(var i=0;i<elemChildsLen;i++){
var elem = elemChilds[i];
var r = elem.getBoundingClientRect();
if(r.top != 0){
eTop = r.top;
i= elemChildsLen;
}
}
}
var nScrollHeight = winHeight - eTop - 20;
return {
'height': nScrollHeight + 'px',
'overflow-x': 'hidden',
'overflow-y': 'auto'
};
}
};
I've tried using a custom directive but binding DOM or writing a watcher isn't a preferable solution for me due to performance. Thanks in advance!
Use one time binding, which will only call styleElement once.
<div class="myElement" ng-style="::styleElement('myElement')">
...
...
</div>
i am using angular Ui grid. i am using cell Template in order to render the data. previously i am using ng-repeat in cell template in order to render the data but ng -repeat causing performance issue in case of large data. so i decided to load the on demand rather than binding it during initilization phase of grid not what i done is i have a one button in my cell template on the click of that button i am creating the html template and setting the newly generated html to the one of the div in cell template using .Inner HTML property. all the html is getting render proper but now problem start here i want to call some function in my controller on the click event of my dynamically added div. but when i click the div nothing is happen and function is not getting call.
here is the code .
for (var i = 0; i < data.length; i++) {
if (data[i]) {
var value;
if (data[i].Value > 100) {
value = +data[i].Value;
} else if (data[i].Value > 0 && data[i].Value <= 100) {
value = data[i].Value + " %";
} else {
value = data[i].Value;
}
html = html + "<div data-ng-show='row.entity.Over.length>0' style='height:25px;margin:0px' class='row' >" +
"<div class='col-lg-6'>" + data[i].Type + " : " + data[i].line + "</div>" +
//"<div class='col-lg-6' ><a class="+classname+" ng-click='grid.appScope.clickHandler()'>" + value + "</a></div>"
"<div class='col-lg-6' ><a ng-click='grid.appScope.clickHandler()'>" + value + "</a></div>" + "</div>";
$compile((html)($scope));
}
}
Please help me on this issue.
I know I've had this happen before but not in exactly this way and I'm having trouble making the necessary changes.
I have a div that starts out empty and hidden as part of a gallery. When I click an image, it populates the div and shows it. Along with the image/content, there's some navigation elements as well. Thing is, those are also dynamically generated and not working since the angular needs to recompile. Here's the function that populates:
$scope.picturePop = function(picID){
match = $.grep($scope.gallery, function(obj) { return obj.id == picID; });
pic = match[0].image;
title = match[0].title;
desc = match[0].desc;
closediv = "<div id=\"divClose\" class=\"floatRight\" ng-click=\"closeParent();\">Close</div>";
navDiv = "";
if(picID > 1){
prev = picID - 1;
navDiv += "<div id=\"picNav\" ng-click=\"picturePop(" + prev + ")\">Previous</div>";
}
if(picID < $scope.picCount){
next = picID + 1;
navDiv += "<div id=\"picNav\" ng-click=\"picturePop(" + next + ");\">Next</div>";
}
$('#innerPictureDisplay').html(closediv + "<br/><br/><img src=\"images/paintings/" + pic + "\" /><p><b>" + title + "</b><\p><p>" + desc + "</p>"+ navDiv);
$('#outerPictureDisplay').css('display','block');
};
How do I make that code "recompile" so that the "CLOSE" and navigational items work?
UPDATE
So, I changed my approach but I'm clearly still missing something:
my new HTML:
<div id="outerPictureDisplay" ng-show="picID > 0">
<div id="innerPictureDisplay">
<div id="divClose" class="floatRight" ng-click="picID = 0;">Close</div>
<div id="picNav" ng-click="picturePop({{prevID}});" ng-show="picID > 1">Previous</div>
<div id="picNav" ng-click="picturePop({{nextID}});" ng-show="picID < picCount">Next</div>
<img src="images/paintings/{{thisPic.image}}" />
<p>
<b>{{thisPic.title}}</b>
</p>
<p>{{thisPic.desc}}</p>
</div>
</div>
my new function:
$scope.picturePop = function(picID){
match = $.grep($scope.gallery, function(obj) { return obj.id == picID; });
$scope.thisPic = match[0];
$scope.picID = picID;
$scope.nextID = picID + 1;
$scope.prevID = picID - 1;
var $content = $('#innerPictureDisplay');
var scope = $content.scope();
$compile($content.contents())(scope);
};
When I click on a pic, it all loads up fine but when I click on a Previous or Next, it closes since it's registering picID as null. I checked and the numbers are indeed getting printed in the code, but angular doesn't see them on the click. They're coming through as undefined - seemingly the compile issue. I tried implementing it all as a directive, even using the suggested link, but I must've done something off since it didn't help.
You can use the angular service $compile like so:
$compile("<a ng-click='myFunction()'>my html</a>")(scope);
And of course you must inject him wherever you are.
Anyway, for God sake, don't use DOM manipulation this way. You should use a directive and feed the content from a model instead.
UPDATE 1
I sugest use incuna's bindHtmlCompile directive that you can found here.
I have this user list, which can grow dynamically if you add a new user. Now I want my whole body to be the length of the user list.
In my factory I determine the length of the array and now I need to know how to access/translate this to the view in angular. Something like this:
<div class="mainWrapper" id="mainView" style="width: {{gridSizeNG.x * 122}}px; height: {{gridSizeNG.y * the length of the user list }}">
...
</div>
Part of my factory code:
UserService.getUsers = function () {
$http.get("api/users") //your API url goes here
.success(function(dataFromServer){
//console.log('LOGGING DATADROMSERVER ', dataFromServer);
//UserService.userList = [];
/*dataFromServer.forEach(function(user, index, arr) {
UserService.userList.push(user);
})*/
var initials = function(name){
var d1 = name.split(" ")[0].charAt(0).toUpperCase();
var d2;
try
{
d2 = name.split(" ")[1].charAt(0).toUpperCase();
}
catch(e){
d2 = "";
}
return d1 + d2;
console.log('LOGGING INITIALS ', d1 + d2);
}
for (var i = 0; i < dataFromServer.length; i++) {
UserService.userList[i] = dataFromServer[i];
UserService.userList[i].initials = initials(UserService.userList[i].name)
};
console.log('#### logging lenght of the userlist ' , UserService.userList.length );
//here you should update the usersList from the server like this:
//UserService.usersList = dataFromServer;
return dataFromServer;
})
.error(function(errorFromServer){
//something went wrong, process the error here
console.log("Error in getting the users from the server");
})
};
return UserService;
})
As you can see from the image, the height of the body is now not dynamic
IMO the best approach here would be to use CSS to your advantage and have the size grow in accordance with the amount of elements you have. Which means (idealy) you don't need to set the height and width property in CSS, you only need to set the max-height and max-width properties, should the list grow more than the body.
If you do want to control the height and width using JS then use ng-style instead of style, keep in mind ng-style is considered a last resort in controlling styles, one should ideally use ng-class if possible.
With ng-style you will be able to use your scope variables to control the height and width.
Also try to use something like this, using your existing html code:
<div class="mainWrapper" id="mainView" style="width: {{gridSizeNG.x}} * 122 + 'px'; height: {{gridSizeNG.y}} * {{the length of the user list }} + 'px'">
...
</div>
Not sure if the above code will work, but ng-style will.