I've built a report which uses a set of nested ng-repeat directives to create what can be an enormous table. It works well to build the table, but after that, the scrolling performance is affected greatly - I assume this is due to what must be the large number of watches created by the ng-repeats.
The report only has to be built once and then is static. I don't need to watch the data continually.
I have two questions:
A) is there anyway to see a list of all the variables Angular is currently watching?
EDIT: This post was a great help in learning how to benchmark
B) Is there anyway to tell Angular to stop all of the watches it is doing? I've seen lots of posts about canceling watches which one sets up oneself, but that these are native directives, I'm not sure how I'd tap into them.
My preference would be to have a variable that I can say "if truthy, then do all watches, if not, then do not watch" or a function to just say "start watches" and "stop watches".
I have set up a very nice DOM watching service which can tell when all of the ng-repeats have executed, so I can know when I want to stop watching.
This is the table itself. Aside from tk-ng-repeat-completed, the other "tk-" attributes are just used for data and aren't actually directives.
<div class="table-responsive">
<table tk-sticky-column id="records" class="table table-striped table-hover table-condensed">
<!-- tbody[n] -->
<tbody class="dataset" ng-repeat="dataset in report.data track by $index" tk-ng-repeat-completed>
<!-- row[0] -->
<tr class="headline">
<!-- header[0] label -->
<th class="headline" style="background-color:#042E34;">
<div style="width:200px;"><h4>{{report.labels.y[$index]}}</h4></div>
</th>
<!-- header[n] label -->
<th ng-repeat="x_label in report.labels.x" tk-ng-repeat-completed
class="datapoint date"
tk-raw-data="{{x_label}}">
<em><small>{{x_label}}</small></em></th>
<!-- header[last] space for addition #todo remove this, add during calculations -->
<th class="date"></th>
</tr>
<!-- row[n] -->
<tr ng-repeat="(key, datapoints) in dataset" tk-metric-key="{{key}}">
<!-- column[0] label -->
<td tk-metric-key="{{key}}"
tk-calc="{{report.labels.data[key].calc}}"
class="rowdesc begin">{{key}}</td>
<!-- column[n] data -->
<td ng-repeat="datapoint in datapoints track by $index" tk-ng-repeat-completed
ypos="{{$parent.$parent.$parent.$index}}" xpos="{{$index}}" tk-metric-key="{{key}}"
class="datapoint"
tk-raw-data="{{datapoint}}">
{{datapoint}}</td>
</tr>
</tbody>
</table>
</div>
I'd recommend checking out the single-bind syntax they introduced in 1.3, if you're on a version greater than or equal to that one. It works really well and is very simple to implement. Here's an example:
normal syntax, which creates a watcher for each variable:
<div ng-repeat="foo in vm.bar">
{{foo}}
</div>
single-bind syntax, with no watcher on the nested variable inside the repeat:
<div ng-repeat="foo in vm.bar">
{{::foo}}
</div>
edit:
I forgot to mention that if your data isn't going to change at all after it gets populated the first time (e.g. from a $http.get), you can also just use the single-bind syntax on the top level ng-repeat:
<div ng-repeat="foo in ::vm.bar">
{{::foo}}
</div>
Related
I'm trying to use angular-smart-table for grid in my new AngularJS app. According to the document, to sort a column, I should use the st-sort directive like bellow:
<th st-sort="firstName">first name</th>
<th st-sort="lastName">last name</th>
However, I'm trying to re-use the piece of code for not only one table, so I don't know the table field names in advance until the run-time. I'm doing something like bellow:
<script type="text/ng-template" id="content1">
<div ng-repeat="table in $ctrl.tables">
<h2>{{table._tableName}}</h2>
<table st-table="table._data" class="table table-striped">
<tr>
<th ng-repeat="fieldName in table._fieldNames" st-sort="{{fieldName}}">{{fieldName}}</th>
</tr>
<tr ng-repeat="data in table._data">
<td ng-repeat="fieldName in table._fieldNames">{{$ctrl.formatCell(table, data, fieldName)}}</td>
</tr>
</table>
</div>
</script>
And this cannot work (cannot sort, other functions OK). I tried bellow it does not work, seems the st-sort has to be in the <th> tag.
<th ng-repeat="fieldName in table._fieldNames"><span st-sort="{{fieldName}}">{{fieldName}}</span></th>
And bellow does not work as well:
<tr>
<span ng-repeat="fieldName in table._fieldNames">
<th st-sort="{{fieldName}}">{{fieldName}}</th>
</span>
</tr>
Today I tried to develop a directive and use it in the comment by setting restrict to "M" to solve the above. Then I got a new problem: I'm using UI-Router in this app and I cannot get the table contents in my directive, because UI-Router states have isolated scopes and it only supports controllers but does not support directives. The author may think supporting directives is not necessary (yes in most cases, but this kind of assumptions are always dangerous).
I'm Trying two possible ways: 1., put the field names to the session/local storage for sharing as a work-around; 2., abandon UI-Router. Appreciate anyone providing a better solution.
I have an ng-repeat table with a handful of columns. One of te columns is an image field. Here is a snippet with the image column and one of the others:
<tbody>
<tr ng-repeat="insight in insights | orderBy:sort track by $index">
<td>
<select ng-model="insight.type" ng-change="setType(insight)" required="required">
<option value="INSIGHT">Insight</option>
<option value="EVIDENCE">Evidence</option>
</select>
</td>
<!--Image thing-->
<td>
<a ng-href="{{:: insight.imgurl}}" target="_blank"><img class="full" wb-lazy-load-image="{{:: insight.imgurl}}" data-height="600" data-width="800"></a>
</td>
...
</tbody>
As it stands, when I click to sort by a column, all of the columns sort with their associated row, except for the image row; it stays in the same spot.
If I remove the track by $index code at the <tr> it reorders all the columns as expected.
Any idea why or what I can do to fix that?
Edit:
FWIW - sort is defined in my Controller as: $scope.sort = ['-primary', '-live'];
Edit (Solution):
I ended up removing the one time binding as suggested in the accepted answer and it fixed the issue. However, after some consideration, we decided we'd rather have the performance gain of our one time, lazy loads than the track by performance as the lists would be small.
For anyone else who has this issue, one time binding with the images in conjuction with track by $index seemed to be the issue in this case.
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="orderByExample">
<script>
angular.module('orderByExample', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.friends =
[{name:'John', phone:'555-1212', age:10,sort:['-age','-name']},
{name:'Mary', phone:'555-9876', age:19,sort:['-age','-name']},
{name:'Mike', phone:'555-4321', age:21,sort:['-age','-name']},
{name:'Adam', phone:'555-5678', age:35,sort:['-age','-name']},
{name:'Julie', phone:'555-8765', age:29,sort:['-age','-name']}];
}]);
</script>
<div ng-controller="ExampleController">
<table class="friend">
<tr>
<th>Name</th>
<th>Phone Number</th>
<th>Age</th>
</tr>
<tr ng-repeat="friend in friends | orderBy:sort track by $index">
<td>{{friend.name}}</td>
<td>{{friend.phone}}</td>
<td>{{friend.age}}</td>
</tr>
</table>
</div>
</body>
According to the AngularJS Doc:
Note: track by must always be the last expression:
https://docs.angularjs.org/api/ng/directive/ngRepeat
An example which might help you:
<div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
{{model.name}}
</div>
It appears as though the one time binding syntax is the culprit. using {{::insight.imgurl}} would create a one time binding, where the watcher is removed from this element. However, when used in combination with wb-lazy-load-image, it would appear that the directive believes it is getting a two way binding but is in fact getting a one way binding.
My best guess without seeing how this directive works is that the directive takes the insight.imgurl and performs a network call to find the image. Then the filter causes the array to change order, but the directive isn't aware that the order has changed, due to the one way binding. Therefore, the directive loads the image in the incorrect place. Removing track by $index would cause the ng-repeat to be handled differently in the $digest loop, causing the problem directive to be completely re-rendered.
Removing :: from the binding to the directive input should allow the directive to function as intended, even without a full re-render. In general, :: is only intended for use in cases where the expressions will be permanent after rendering; in this case, they are not permanent due to the order filter.
I am trying to implement infinity type scrolling on a simple grid. Html is like this:
<div class="text-center" style="background-color:#eee;">
<H2 style="margin-top:0px;">Customer List</H2>
<hr />
Search: <input ng-model="customerVm.search" /> <br /> <br />
<table class="table table-hover" ui-scroll-viewport style="height:500px;">
<tbody>
<tr ui-scroll="customer in customerVm.datasource" ng-model="customerVm.gridResult" "buffer-size="10">
<td>
</tbody>
</table>
</div>
I want to filter what is in the grid based on the search box model, seems simple enough. I would normally just use the following with an ng-repeat
<tr ng-repeat="customer in customerVm.datasource | filter:customerVm.search" ng-model="customerVm.gridResult" "buffer-size="10">
<td>{{customer.customername}}</td>
</tr>
However with the use of "ui-scroll" instead of ng-repeat I am getting an error
Expected uiScroll in form of '_item_ in _datasource_' but got 'customer in customerVm.datasource | filter:customerVm.search'
Am I missing something on how to filter the data using Angular-ui scroll? I see the line in the scroll lib that is causing the issue which throws an error if there is anything other then
item in datasource
This makes me think that filtering is not possible in Angular-ui scroll. Any ideas folks?
Filtering is unfortunately not yet possible in this way, as the error suggests. See "Parameters" in the Readme file. You could implement your own filter in the data source "get" function. See also the official examples.
I have a problem with ng-repeat and ng-init. It doens'nt show me the content nor the ID when i ask for them in my code (project.content). But it doesn't show the text itself either. So somewhere something is going wrong. Note that I use other open and close tags for angular as default.
Appareantly Angular sees something different in the code as the browser or I do, as I recieve this console error:
http://errors.angularjs.org/1.2.12/$parse/ueoe?p0=projects%20%3D%20%7B
<table ng-init="projects = {" id":"1","content":"testtesttest"}="" "="">
How can I get this to work?
HTML code:
<div ng-app="overviewApp">
<table ng-init="projects = <?=json_encode($projects)?> ">
<tr ng-repeat="project in projects">
<td>[{[project.content]}]</td>
</tr>
</table>
</div>
Looks like this when in browser source code:
<!-- BEGIN PAGE CONTENT-->
<div class="row">
<div class="col-md-12">
<div class="content-section">
<div ng-app="overviewApp">
<table ng-init="projects = {"id":1,"content":"TestTestTest"} ">
<tr ng-repeat="project in projects">
<td>[{[project.content]}]</td>
</tr>
</table>
</div>
It cannot parse it because it is seeing double quotes inside double quotes. Try using single quotes.
<table ng-init='projects = <?=json_encode($projects)?> '>
Also you must ensure that projects is in array format like so:
ng-init='projects = [{"id":1,"content":"TestTestTest"}]'
Finally, since you are using a custom interpolate providers {[{ and }]}, you must define that in your config:
app.config(['$interpolateProvider', function ($interpolateProvider) {
$interpolateProvider.startSymbol('[{[');
$interpolateProvider.endSymbol(']}]');
}]);
See it working: DEMO
Alternatively, instead of using interpolation markup, I would recommend using the ng-bind directive like so:
<td><span ng-bind="project.content"></span></td>
See a DEMO.
NOTE: The AngularJS documentation recommends using a controller over the ng-init directive to initialize values on a scope.
I wanted to have the functionality of rearranging rows in a table (sorting rows using drag and drop).
And the index of the row arrangement should also change in the model.
How can I do something similar to this : http://jsfiddle.net/tzYbU/1162/
using Angular Directive?
I am generating table as :
<table id="sort" class="table table-striped table-bordered">
<thead>
<tr>
<th class="header-color-green"></th>
<th ng-repeat="titles in Rules.Titles">{{titles.title}}</th>
</tr>
</thead>
<tbody ng-repeat="rule in Rules.data">
<tr>
<td class="center"><span>{{rule.ruleSeq}}</span></td>
<td ng-repeat="data in rule.ruleData">{{statusArr[data.value]}}</td>
</tr>
</tbody>
</table>
I did it. See my code below.
HTML
<div ng:controller="controller">
<table style="width:auto;" class="table table-bordered">
<thead>
<tr>
<th>Index</th>
<th>Count</th>
</tr>
</thead>
<tbody ui:sortable ng:model="list">
<tr ng:repeat="item in list" class="item" style="cursor: move;">
<td>{{$index}}</td>
<td>{{item}}</td>
</tr>
</tbody>{{list}}
<hr>
</div>
Directive (JS)
var myapp = angular.module('myapp', ['ui']);
myapp.controller('controller', function ($scope) {
$scope.list = ["one", "two", "thre", "four", "five", "six"];
});
angular.bootstrap(document, ['myapp']);
There is another library: RubaXa/Sortable: https://github.com/RubaXa/Sortable
It is for modern browsers and without jQuery dependency. Included is a angular directive. I'm going to check it out now.
You get good touch support additionally.
AngularJS was not really built for the manipulation of DOM elements, rather to extend the HTML of a page.
See this question and this Wikipedia entry.
For DOM manipulation, jQuery/mootools/etc will suite you just fine (hint: the example in your jsFiddle link).
You could probably use AngularJS to keep track of the ordering of your elements to update your model. I'm not sure how to do this using directives, but the following code may be useful
var MyController = function($scope, $http) {
$scope.rules = [...];
...
}
var updateRules = function(rule, position) {
//We need the scope
var scope = angular.element($(/*controller_element*/)).scope(); //controller_element would be the element with ng-controller='MyController'
//Update scope.rules
}
Then when you reorder the list, simply call updateRules() with the changed rule and its new position in the model.
Anyone else who wants something like this but not understanding the accepted answer. Here is a directive UI.Sortable for AngularJS that allows you to sort an array/ table rows with drag & drop.
Requirements
JQuery v3.1+ (for jQuery v1.x & v2.x use v0.14.x versions)
JQueryUI v1.12+
AngularJS v1.2+
Usage
Load the script file: sortable.js in your application: (you can find this sortable.js from here
<script type="text/javascript" src="modules/directives/sortable/src/sortable.js"></script>
make sure you have included JQuery, AngularJs and JQueryUI js files in
order before this sortable file
Add the sortable module as a dependency to your application module:
var myAppModule = angular.module('MyApp', ['ui.sortable'])
Apply the directive to your form elements:
<ul ui-sortable ng-model="items">
<li ng-repeat="item in items">{{ item }}</li>
</ul>
Developing Notes:
ng-model is required, so that the directive knows which model to update.
ui-sortable element should contain only one ng-repeat
Filters that manipulate the model (like filter, orderBy, limitTo,...) should be applied in the controller instead of the ng-repeat
3rd point is very Important as it took almost an hour to understand
why my sorting was not working?? It was because of orderBy in html
and that was resetting the sorting again.
For more understanding you can check the detail here.
If I understand you correctly, you want to be able to sort the rows? If so, use UI-Sortable: GitHub
Demo: codepen.io/thgreasi/pen/jlkhr
Along with RubaXa/Sortable there is one more angularjs library avilable that is angular-ui-tree. Along with drag and drop we can arrange elements in a tree structure and we can add and delete elements (nodes)
Please see the this link for examples
http://angular-ui-tree.github.io/angular-ui-tree/#/basic-example .
Please see this for github
https://github.com/angular-ui-tree/angular-ui-tree.