New bindings in already compiled page - angularjs

I have a problem using the footable plugin with angular,
the problem is that footable appends new table rows to my table, but it pastes raw html text instead of angular replaced values, I mean {{'COMMENT'|translate}} instead of 'Comment' (see 'appended table image')
appended table image
I added click event, to perform special action to reevaluate, but I am not sure how to rebind the element HTML.
Table html
<th data-hide="phone,tablet" >{{'QUANTITY'| translate}}</th>
...
<tr ng-repeat="item in items | filter:itemsFilter" on-last-repeat ng-click="rowExpanded($event)" >
<td>{{item.Stock}}</td>
...
</tr>
Please let me now if you need more info. I'm quite stuck, tried googling, but i thing I don't know the term of my problem.
EDIT: It seems to me that footable caches table headers on init, and then reuses those values that are not compiled by angular yet, maybe that could help to find the answer.
Problem is that angular doesn't know that details row exist, and bindings inside it aren't replaced with values
expanded first row with console view

I figured out, that I need to recompile next element that is appended to the footable, I added ng-click on table rows:
<tr ng-repeat="item in items | filter:itemsFilter" on-last-repeat ng-click="rowExpanded($event)" >
Then in the controller, I found that currentTarget.nextElementSibling will return, next table row, which happens to be that uncompiled, new details row, so $compile does the trick. However this doesn't work when the row is already expanded and window being is resized, added columns in the details row will be in {{variablename}} syntax. But I guess I can live with that downside
$scope.rowExpanded = function($event){
$timeout(function(){
console.log($event.currentTarget.nextElementSibling);
$compile($event.currentTarget.nextElementSibling)($scope);
});
};
EDIT: Ok this didn't work when I used the <progressbar> template. Because at the time when footable reads data it is just a bunch of divs, and when i ask angular to compile those, it cant compile because bunch of divs isn't a >progressbar<
But yet, I found another solution, I delayed the footable initialization after everything is done on angular side:
TL;DR; I removed class 'footable' from table so it wont initialize automatically and call .footable() on the table element only after all of the ng-repeat rows were added (and compiled) to the table. This solves all my current footable/angular relation problems.

Try spacing out your translation statement, so instead of
{{'COMMENT'|translate}}
try
{{ 'COMMENT' | translate }}

Related

Filter options in 'Selenium Page Object Generator' (Chrome Plugin)

I need to generate a Page Object Model for a page to use with Selenium WebDriver. My page is so complex that the 'Selenium Page Object Generator' plugin generates 5000 lines of code from the code body without any filter.
My Requirement: I need to filter and retrieve only the page objects from the code block under HTML id = "xyz" (example).
My Question: What should be the filter criteria under 'Root Selector' in the above options window? Or is there any other options that I must try.
Note: I am raising this question due to the lack of information/guidelines available to use this plugin. Any informative links are also appreciated.
Try adding (body) in the root selector text-box
After trying the plugin for a little bit I don't think the root selector accepts operators like (=)
What you can do is to narrow your criteria a little bit
Example :
You need to get all the web elements under a certain table
You can add (table,tbody,td) in the root selector and the tool will get all the Elements under all the tables in your page having below hierarchy :
<table>
<tbody>
<td>
Element1
Element2
Element3
</td>
</tbody>
</table>

Decompile angular elements

We have a angular grid written by some guys here at work, the entire company uses it.
A td-cell could look like this
<td typeahead-cell="location as location.Name for location in getApiLocations($viewValue, mapping)" ng-model="mapping.selectedLocation">
{{mapping.getLocationNames()}}
</td>
The typeahead-cell directive will execute some custom code on the td, what it does is hookup some code so that if you double click or write in the cell it will go from display only to (in this case) typeahead. It does this by taking the html in the td cell (The td cell is already compiled by angular), wrap it with some custom code that does above functioanlly and then call $compile on the entire thing. This works with expressions above like {{mapping.getLocationNames()}} because they do not change when compiling so it can be compiled any number of times.
The problem I face now is that I try to use a more complex expression with ng-repeat. Problem is the first compile (Done directly by angular-core) will change html from example
from
<span ng-repeat="location in mapping.locations">...</span>
to
<!-- ngRepeat: location in mapping.locations -->
Then when our custom grid code executs it will try to compile the code above which will result in an empty since it compiles against a html comment.
This is the code that breaks
$element.html($compile(displayElement.html($element.html()))($scope));
$element is the td-cell that contains my orignal code that, when doing $element.html() it will take compiled code and try to use that. Wont work. Displayelement is a wrapper that will show when we are in displaymode.
I either need to decompile $elementbefor edoing $element.html or somehow move the content of the $element (td cell) compiled and hooked up.
Any ideas?
edit: I have somewhat solved it, doing this
$element.children().appendTo(displayElement);
displayElement.appendTo($element);
This will take the children from the td-cell and add them to the displayElement without actually breaking the original $compile. jQuery.children cant move <!-- comment --> elements so if you have an expression with ng directives like my repater above you need to wrap it in a dummy element like
<span><span ng-repeat="location in mapping.locations">...</span></span>
Any workaround for this?
Instated of that line if you can check with this
//Store it first on a variable if blank
var html;
if(!html) html = displayElement.html($element.html());
$element.html($compile(html)($scope));
Hopefully it will work. May be you need to manage the scope of the variable.
Final solution is this
$element.contents().appendTo(displayElement);
displayElement.appendTo($element);
It's very important to use contents and not children because childrenwill ignore text nodes which will not include the comments generated by ng-repeat directive.

Why is my angular grid so slow?

So, I have made some custom directive which draws kind of a data-grid, based on floated divs (because nested flex implementation in FF sucks - but it's not the point).
How it works :
I pass some data collection to the directive via something like <the-grid data-list="parentController.displayedRows">
Inside this first directive, I have columns via something like <a-grid-column data-value="row.value"></a-grid-column> with many attributes I won't precise here.
The data-value value can be a direct expression, bound to the row on which the the-grid directive controller is ng-repeating in order to display each columns, or a function which have to be $eval-uated in order to display the intended value from the parentController.
In my <the-grid> directive controller, I have the rendering template of my grid which make a nested ng-repeat div (first one on the rows of the data-collection, second one on the columns, created in the directive), it looks like :
<div data-ng-repeat="row in list">
<div data-ng-repeat="cell in theGridColumns"
data-ng-bind-html="renderCell(row, cell)">
</div>
</div>
I have some keyboard nav in order to quickly select a row or navigate within pagination or many tabs, which does nothing more than applying some class on the selected row, in addition to update some "selectedRowIndex".
I'm using angular-vs-repeat in order to have the minimum of row divs in my DOM (because the app is running on slow computers). This works well.
The problem is that every time I'm hitting some "up" or "down" key on my keyboard, Angular is "redrawing" EVERY cells of the list.
So, let's suppose I've 200 rows in my data list, and 7 columns for each rows. First load of the page, it passes ~3000 times in the renderCell() function. Ok , let's accept that (don't really understand why, but ok).
I hit the down key in order to go to the second line of my list. It passes ~1100 times in the renderCell() function.
So yes, the result is very slow (imagine if I let the down arrow key pressed in order to quick navigate within my rows)... I can't accept that. If someone could explain that to me... Any help would be greatly accepted :)
If I make the same thing without a directive (direct DOM manipulation, with columns made by hand and not in a ng-repeat within a ng-repeat), every thing is smooth and clean.
Yes, I've look into every angular grid on the market. No one is satisfying me for my purpose, that's why I've decided to create my own one.
And no, I really can't give you some JSFiddle or anything for the moment. The whole app is really tentacular, isolating this is some kind of complicated).
Try to use bind once (angular 1.3+)
<div data-ng-repeat="row in ::list">
<div data-ng-repeat="cell in ::theGridColumns"
data-ng-bind-html="::(renderCell(row, cell))">
</div>
</div>

How to implement pagination in Angular when collection size is changing

I am trying to use angular-UI pagination for a table, where I am splicing the array where the page intersects the array. However I am not clear how to handle the fact that the array of objects is being added to and deleted to from within the table itself.
Also, I am sorting through a filter by the name property of an object in my ng-repeat. I expect what's is item[0] in my model array, does not correlate with the first item being displayed in the table via ng-repeat...I could be wrong.
I am just looking for advice on how to implement paging when the ng-repeat sorts the list, and the collection size is changing all the time.
Regards
I
you can try to use multiple filters at once for example:
ng-repeat="item in items | myFilter | limitTo:pageNumber*perPage | limitTo: -perPage"
This allow you to use your filter first on each collection/model change, then it show last "perPage" records dependig of "pageNumber".
Please see that demo : http://plnkr.co/edit/3nqRHUySsJZwpKQiMMTm?p=preview
just set watch to yourCollection.length
ie:
$scope.$watch('yourCollection.length', function(){
$scope.bigTotalItems = $scope.yourCollection.length;
}
)

How do I prepend a new item to an ng-repeat list?

I have situations where I need to prepend an item to a list that is initially generated using ng-repeat. How do I do this?
<div ng-click="prependItem()>Click Here</div>
<div ng-repeat="item in items">
<div class="someClass">Item name: {{item.name}}</div>
<div class="anotherClass">Item type: {{item.type}}</div>
</div>
If I click on prependItem() I want want the new item to be added to the top of the list. Obviously, I don't want to regenerate the entire ng-repeat. I've been unable to find any documentation that would explain how to do this. Thank ahead of time for any help!
scope.prependItem = function (newItem) {
items.unshift(newItem);
};
AngularJS is smart enough to know the addition, and only create html element for it
http://plnkr.co/edit/qzIfzSP6buiQ49rDreNk?p=preview
You can see from console that only the newly added item will log messages
ng-repeat will rebuild the list if you add an item to the front of your array. If you add it to the end it's smarter in that it will only update the DOM to reflect the change for the one item you've added in.
Because you've added an item to the front, it has to move everything in the DOM so I think it just rebuilds it as it's easier to do than moving (don't quote me on that though lol). It isn't necessarily a bad thing to rebuild that list (unless it's huge, you won't even notice the refresh, if it is very big, I'd recommend showing a spinner whilst it rebuilds the html, that's the approach we've taken at work; since the user has clicked the button they're expecting the interaction so the spinner seemed like the best compromise whilst angular rerendered).

Resources