How to get width of a dynamically created element with $comiple in Angularjs - angularjs

So I want to create an element in angular and append it to DOM :
//in my directive I've created an element as bellow :
el = $compile('<span>Hello dynamically</span>')($scope);
// and then I appended it to my DOM like this :
myDomElement.append(el);
So far so good , no errors no nothing .
All I'm looking for is to how to get the width of this el that I've created in my directive ?
Normally when I want to get width of an element I'll get the offsetWidth of that element ,
I'm looking for something like this :
el = $compile('<span>Hello dynamically</span>')($scope);
var width = el.offsetWidth ; // I want the width here !!
console.log(width); // will log 0 !!!!!!
console.log(el); // will show an Object that has a **offsetWidth:134** property !!!!!!!
myDomElement.append(el);
But when I log this , I always get 0 , it's weird because when I look at my developer tools in chrome I can see that offsetWidth is not 0 !

Do this :
el = $compile('<span>Hello dynamically</span>')($scope);
myDomElement.append(el);
var width = el.offsetWidth ; // I want the width here !!
console.log(width);
Means first append the element to DOM , then get it's width

Related

Target input field in document and set & remove a class name if input field has any value

I have a bunch of input fields into which I'd like to set a someClass name only in condition if an input field has some value. Remove someClass name if it doesn't.
I'm currently studying manipulating forms and forms fields using JavaScript and this is the code I'm working on.
// Add a class to an element, without removing/affecting existing values
var inputs = document.getElementsByTagName('input');
for (var i = 0; i < inputs.length; i++) {
// Set HTML elements collection index?
var input = inputs[i];
input.addEventListener('input', function() {
if (input.value.length > 0 && !input.className.match(/(?:^|\s)someClass(?!\S)/)) {
console.log("Input has some value");
input.className += "someClass";
}
if (input.value.length == 0) {
console.log("Input is empty");
input.className = input.className.replace( /(?:^|\s)someClass(?!\S)/g , '' );
}
});
}
The current code do what it suppose to do only on last input field. It occurred to me that var input is overwritten every time there is a new loop.
So I was thinking to target current element by some conditional statement within the loop. I can't think of anything else (with little knowledge I have about this) except manually assigning some of input's attributes.
The issue you are running into is that each input event listener is updating w/e element the variable input is representing at the time the event is fired, and is therefore only updating that element.
So the timeline goes like this:
Add event listener to input 1 | input = input1
Add Event Listener to input 2 | input = input2
Add event listener to input 3 | input = input3
All 3 event listeners are going to change the element held by variable input, which at this time is only input3.
User clicks inside input and types
var input which is input3 gets updated
As Sean pointed out, you need each eventListener function to update the specific element that fired that event. Here you have 3 options:
Use this this keyword
https://jsfiddle.net/5gbnykme/1/
Use event.target as reference
https://jsfiddle.net/5gbnykme/2/
Use a wrapper function to scope your variable locally
https://jsfiddle.net/5gbnykme/3/
TL;DR Make sure the element called in your event listener is the element that triggered the event.

Selenium java code: How to read text from hidden element that contain many tags

1.<svg blablabla style:overflow: hidden > <aaa id=aaa>
2.+
3.<g>
4.<g>
5.<g>
6.<g>
The tags are inside the svg.
1 is the hidden tag, i want to get the 5th line text(), this is what i do.
WebElement hiddenDiv = driver.findElement(By.id("aaa"));
String n = hiddenDiv.getText();
String script = "return arguments[0].innerHTML";
n = (String) ((JavascriptExecutor) driver).executeScript(script, hiddenDiv);
System.out.println(n);
how can i get g[2], i have tried by direct xpath and it got an error because the svg is hidden.
You just need to grab an array of the g tags and pick the one you want. If you want the 5th, you would use the code below.
List<WebElement> gs = driver.findElements(By.cssSelector("#aaa g"));
System.out.println(gs.get(4).getAttribute("innerHTML"));

Array whose each clip is linked to its equal clip of another array

Hello I apologize in advance for my question which I'm sure is pretty basic.
On a map are set 33 landmarks with an array calling a class in the library.
A second array defines the coordinates of those landmarks.
for (var i:uint = 0; i < 33; i++) {
mark[i] = new landMark();
landMarks.addChild(mark[i]);
mark[i].x = lmxy[i]['x'];
mark[i].y = lmxy[i]['y'];
}
var lmxy:Array = [{x:1620,y:880},{x:1850, y:1050},etc...];
So far so good, the landmarks show each in its right place.
The third array contains different legends supposed to show when a landmark is clicked.
So the landmark [1] should show the legend [1] and the landmark [31] the legend [31]
var lgd:Array = [lgdA, lgdB, etc... ];
var legends:MovieClip;
for (var j:uint=0;j<lgd.length;j++) {
legends = new lgd[j]();
legends.x = 300;legends.y = 170;
}
Edit cause obviously that was unclear :
I tried that in the loop to link the marks to the legends but I get an error :
mark[i].addEventListener(MouseEvent.CLICK, getLgd);
function getLgd(e:Event):void {stage.addChild (lgd[i]);}
Any help would be very welcome !
The problem is that the variable i doesn't have a definition. The only way for you to find out which of the landmarks were clicked is to find its index in the array, then you can add the legend that has the same index. Because the index isn't passed from the event listener, you need to use e which has the target property.
This should do the trick:
mark[i].addEventListener(MouseEvent.CLICK, getLgd);
function getLgd(e:Event):void
{
var i:int = mark.indexOf(e.target);
stage.addChild(lgd[i]);
}

AngularJS : Why the data is not displayed in view may I use $scope.apply?

I am getting data from service and display on view using ng-repeat .Actually I am getting event when user scroll to bottom mean when user reached to bottom I will do something.When It reached to bottom I am changing the contend of my array .I am getting the correct contend in ng-repeat array (display array) but it is not reflect on view why ? May I use $scope.apply() or $scope.digest()
Here is my code
http://plnkr.co/edit/XgOxJnPXZk4DneJonlKV?p=preview
Here I am changing the contend of my display array which is not reflect on view
if (container[0].offsetHeight + container[0].scrollTop >= container[0].scrollHeight) {
if(scope.var<scope.arrays.length)
scope.display=[];
var nextvar =++counter;
var increment=counter+1
console.log("nextvar:"+nextvar+"increment:"+increment)
scope.display=scope.arrays[nextvar].concat(scope.arrays[increment]);
console.log(scope.display)
}
As #Claies mentioned you should use apply(). Though the digest() would probably have worked as well.apply() calls digest() internally. He also mentioned that your variable that seems to be storing the page number gets reset to 0 each time you scroll. You should store that in a scope variable outside that handler.
I tried to fix with minimum change
http://plnkr.co/edit/izV3Dd7raviCt4j7C8wu?p=preview
.directive("scrollable", function() {
return function(scope, element, attrs) {
var container = angular.element(element);
container.bind("scroll", function(evt) {
console.log('scroll called'+container[0].scrollTop);
var counter = scope.page;
if (container[0].scrollTop <= 0) {
if (scope.var > 0)
scope.display = scope.arrays[--scope.var].concat(scope.arrays[scope.var+1]);
}
if (container[0].offsetHeight + container[0].scrollTop >= container[0].scrollHeight) {
if (scope.var < scope.arrays.length)
scope.display = [];
var nextvar = ++counter;
var increment = counter + 1
console.log("nextvar:" + nextvar + "increment:" + increment)
scope.display = scope.arrays[nextvar].concat(scope.arrays[increment]);
console.log(scope.display)
scope.page = counter;
}
scope.$apply();
});
};
})
generally I would have implemented this differently. For example by having a spinning wheel on the bottom of the list that when displayed you get the rest of data.
It is difficult to give you a full working plunker. Probably you should have multiple JSON files in the plunker, each containing the data for one page so that we can add the data to the bottom of the display list.
After you modify display array you just have to call scope.$apply() so that it runs the $digest cycle and updates the view. Also you need the initialize scope.var either in your controller or the directive and modify it conditionally.
I dont if this is what you want. I have modified the plunker take a look.
http://plnkr.co/edit/J89VDMQGIXvFnK86JUxx?p=preview

how to show only 25 element at one time?

I am trying make table view in ionic using angular js .but I have lot of data to show around 5000 object .So after getting data my UI hang because it is printing the data on dom.So I want to implement lazy loading so that only 100 element is visible to me or only 100 element present in dom only .When user scroll it download another 100 objects or element and removing the uppers element so that my UI not hang .can we do in angular js
I will show you how my UI hang .take a look my example here
my loader hang for around 5 seconds ..
So I tried with infinite scroll in my demo so that I am able to show only 100 element at one time
here is my code of infinite scroll But I am not able to display 100 element at one time I didnot get any error also
<ion-infinite-scroll ng-if="!noMoreItemsAvailable" on-infinite="canWeLoadMoreContent()" distance="10%">
<div class="row" ng-repeat="column in _list | orderBy: sortval:reverse | filter: query">
<div class="col col-center brd collapse-sm" ng-repeat="field in column.columns" ng-show="invoice_column_name['index'].fieldNameOrPath===field.fieldNameOrPath">{{field.value}}</div>
<div class="col col-10 text-center brd collapse-sm"></div>
</div>
</ion-infinite-scroll>
</ion-content>
Updated Plunker
I took a little different approach then the others.
There is a variable at the top of the controller called showitems. This controls how many items you want to show at a time. The counter variable will keep track of where how many items are shown. Initially, the value is set to the showitems value since we're going to prepopulate the dataset with the first items immediately in the ShowViewAfterSuccess function.
var showitems = 100;
var counter = showitems;
In your ShowViewAfterSuccess function, I added the data to a scope variable called $scope.total_invoice_records.
Next, I run the first array slice on the data. This will pass the first x number of records to the $scope.invoice_records based on the value set in the showitems variable. This initializes the view with the first set of records.
$scope.total_invoice_records=data.records;
$scope.invoice_records = $scope.total_invoice_records.slice(0, showitems);
I added a loadMore function that simply grabs the next x number of records from the total record set and concatenates them to the current set in $scope.invoice_records and increments the counter. loadMore will also check if there are still more records to load and broadcasts the message to the ionic infinite scroll directive to remove the spinner.
$scope.noMoreItemsAvailable = false;
$scope.loadMore = function() {
var next = $scope.total_invoice_records.slice(counter, counter+showitems);
$scope.invoice_records = $scope.invoice_records.concat(next);
counter = counter+showitems;
if (counter>=$scope.total_invoice_records.length) {
$scope.noMoreItemsAvailable = true;
}
$scope.$broadcast('scroll.infiniteScrollComplete');
};
Most importantly, remember to set the immediate-check attribute on the infinite scroll element to false:
<ion-infinite-scroll immediate-check="false" ng-if="!noMoreItemsAvailable" on-infinite="loadMore()" distance="10%"></ion-infinite-scroll>
You need to set up a pagination rather than use infinite scroll; or keep the functionnality of infinite scroll as is.
but if you wish to still use infinite scroll, then when you load your next datas into _list, just before filling in the new elements, clean your _list with a _list.length= 0
But you will have sideeffects that I don't know such as :
- how to load the 100 first elements ?
- the page will jump from full with 100 elements, cleaned to 0, and filled with next 100. This will lead, I assume, to an unpleasant effect
My configuration of ion-infinite is the following :
<ion-infinite-scroll
ng-if="!myModel.maxItemLoaded"
icon="ion-loading-c"
on-infinite="loadMoreData()"
distance="10"
>
Which means :
- when user scroll gets on the 10 last % of the page height, loads loadMoreData
If user never scoll, then he has only the first data shown.
Edit:
Here is an updated plunkr
http://plnkr.co/edit/B5KCbc8hr66upCXMvXSR?p=preview
Few remarks :
- the infinite scroll directive is independent and shall not surroung your table
- accessing to to index is done by $index, not with 'index'
- the load functionnality has been modified to load the next 100 elements
$scope.populateList = function() {
var size = $scope._list.length;
var maxSize = size + 100;
if (maxSize > $scope.invoice_records.length)
maxSize = $scope.invoice_records;
console.log("populateList",size,maxSize,$scope.invoice_records)
for (var i = size; i <= maxSize; i++) {
console.log("push",$scope.invoice_records[i])
$scope._list.push($scope.invoice_records[i]);
}
console.log($scope._list.length);
$scope.$broadcast('scroll.infiniteScrollComplete');
}
Angular should work with 5000 object, if it is read only , you can use one time binding
An expression that starts with :: is considered a one-time expression.
One-time expressions will stop recalculating once they are stable,
which happens after the first digest if the expression result is a
non-undefined value (see value stabilization algorithm below).
<span ng-bind="::name"></span>
I don't know infinite scrolling, but if I had to implement something like that i would have used ng-filter 'limit'.
ng-repeat="column in _list | limit : limitValue"
And then bind any button/scroll event to
$scope.limitValue = $scope.limitValue + "10";
Ideally you should not have brought this much data in 1 call. Now, if you have, you can achieve this by a small tweak.
Break your data in chunk of 100. And set that 100 in $scope.
var brokenData = [];
var totalData = x; // Total 5000 records
var pages = parseInt(totalData.length/100);
if(totalData.length % 100 !== 0) {
pages += 1;
}
for (var i = 0; i < pages; i++){
if(i == pages - 1) {
brokenData.push(totalData);
} else {
brokenData.push(totalData.splice(0,100));
}
}
Now set
$scope.pages = pages;
And for 1st page or for 1st time,
$scope.pageData = brokenData[0];
$scope.currentPage = 0 + 1;
This will show only 100 records on your page and sets current page as 1.
Now you can choose any pagination tool or bind window scroll events and just update the above 2 things
Like for 2nd page
$scope.pageData = brokenData[1];
$scope.currentPage = 1 + 1;

Resources