knockout.js - use a for binding within a with - javascript

I want to do something like the html below, where I am using a with binding on the table, but within the table I want to do a foreach loop and access each element of the foreach.
When I do it this way, the th tags don't work, and it will only work if I remove the with binding from the html.
How can I get it to work (i.e. render the table headers with values 1, 2, 3)?
<table data-bind="with: myData">
<tr>
<th><input type="checkbox" value=""></th>
<!-- ko foreach: [1,2,3] -->
<th data-bind="html: $data"></th>
<!-- /ko -->
</tr>
<!-- other stuff here -->
</table>

This can happen, if myData is not an observable and is empty, without any value assigned to it.
I have tried your code here and it is working properly.

Related

use variable in knockout js html

I want to use variable in html find but in can't find any way to do it. I tried this but not working for me.
<!--ko foreach: { data: test(), as: 'method'}-->
<!--ko var isValid = test_title;
/ko !-->
<tr class="row"><th colspan="4" data-bind="text: isValid" class="test_title"></th></tr>
<!-- /ko !-->
First Experience with KO.
You can't write javascript in a ko comment, but you can write it in the binding value.

AngularJS: Remove $watch from ng-repeat

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>

Granular rendering control inside a knockout.js `foreach` control binding

I am new to knockout, and I can't seem to find this problem stated or documented elsewhere, though I suspect I might be missing something.
I have a foreach in my knockout template HTML as such:
...
<tbody data-bind="foreach: cfeeds">
<tr class="datarow" data-bind="css:{danger:discovered()==false}">
<td><a title="View this feed" data-bind="text: name,attr: {href: view_url}"></a></td>
<td data-bind="text: description, css:{'text-danger':discovered()==false}"></td>
<td class="text-center" data-bind="text: sched_type"></td>
<td class="text-center" data-bind="text: sched_data"></td>
<td class="text-center" data-bind="text: moment(last_poll()).calendar()"></td>
<td class="text-right">
<div class="feedgraph">
<!-- D3 chart container -->
</div>
</td>
</tr>
</tbody>
...
In the last DIV with class="feedgraph", I add (in this case) a D3 chart, which works fine when the DIV is outside the foreach. However, it appears that knockout will rerender the entire TR every time the JSON poll returns, whether the JSON contains a changed value or not, and my .feedgraph div gets emptied.
Relevant viewmodel blocks:
...
self.cfeeds = ko.observableArray([]);
...
self.loadFeeds = function() {
postJSON(ajax_uri+'get_site_feeds/',{'site_id':selected_site_id},function(d){
self.cfeeds.removeAll();
$.each(d['configured_feeds'],function(i,v) {
self.cfeeds.push(new Feed(v));
});
setTimeout(vm.loadFeeds,5000);
});
};
...
$(function(){
vm.loadFeeds();
});
...
It seems to me that the knockout rendering should occur at the atomic TD level only when a bound value changes, rather than at the TR level as it appears to be doing.
Hence, my question is: Is there a way to keep knockout from rerendering the entire row in a foreach, and instead only rerender the TD cells that have a data-bind attribute and leave my D3 charts alone?

Angular append directive template to table

I have situation when i need to repeat multiple tbody in one table, what im trying to do is to make every tbody directive and i want its template to append to table, but when im put the directive inside the table tag its put his content outside the table.
the cart draw directive:
return {
restrict : 'AE',
templateUrl: 'client/cart/views/cart-draw.html',
scope : {},
replace: true,
controller : controller
}
the tpl:
<tbody ng-repeat="draw in CartService.items.draws track by $index">
<tr>
<td>
//some content
</td>
</tr>
</tbody>
the html:
<table class="table">
<cart-draw></cart-draw>
</table>
here is the plunker, if you inspect element you will see the tbody is out of the table:
http://plnkr.co/edit/9wEGFE5K0w0ayp6qo8Lx?p=preview
That is happening because the <table> tag doesn't recognize your custom <cart-draw> element as a valid child.
I would modify like so: http://plnkr.co/edit/u88N76h5dvLAvR3C1kRs?p=preview
index.html
<table><tbody cart-draw></tbody></table>
cart-draw.html
<tbody ng-repeat="body in bodies">
<tr>
<td>
{{body}}
</td>
</tr>
</tbody>
app.js
$scope.bodies = ["hello1", "hello2", "hello3"];
This is a long pending issue in Angular's Github repo.
https://github.com/angular/angular.js/issues/1459
I also stumbled upon to this problem once (with SVG). It happens because before rendering the directive, the template is cross verified with HTML DTD and alone doesn't make sense (without tag) and so it doesn't work. Same applies to <tr> and <li>
There are many solutions which uses ng-transclude and link functions to wrap it in respective parent tag and then use it.
This is actually a known & strange issue when it comes to directives & <table>'s.
I believe it actually comes in as invalid HTML at first, causing it somehow appear outside of your <table> tag.
Try making cart-draw an attribute of a <tbody>:
<table>
<tbody cart-draw></tbody>
</table>
plunker Example
This will make it work as intended.

Finding an ng-repeat index?

I can do this in Angular.js:
<tr ng-repeat="cust in customers">
<td>
{{ cust.name }}
</td>
</tr>
Which would iterate through all the customers by putting each in a <tr> of its own.
But what if I wanted two customers in one <tr>? How would I accomplish that?
I normally do that by messing around with indexes and modulus values, but I'm not sure how to do that here.
It turns out this can be done without any custom filters or changing the format of your data, though it does require some extra markup. I thought this woudn't be possible at first as you can't use a span or anything similar within the table for your ng-repeat. This answer however, points out that you can use a tbody.
So it's just a case of using the ng-if directive (which renders html if the expression is true), the $index variable (provided by ng-repeat) and the $even variable (which is also provided by ng-repeat and is true when $index is even
I've created a demo in this Plunker
<div ng-controller="MainCtrl">
<table>
<tbody ng-repeat="cust in customers">
<tr ng-if="$even">
<td>{{customers[$index].text}}</td>
<td>{{customers[$index+1].text}}</td>
</tr>
</tbody>
</table>
</div>
This would of course only work if you have two columns, what if you have more? Well you can also put a full expression into ng-if rather than just a variable. So you can use modulus values like this:
<tbody ng-repeat="cust in customers">
<tr ng-if="($index % 3) == 0">
<td>{{customers[$index].text}}</td>
<td>{{customers[$index+1].text}}</td>
<td>{{customers[$index+2].text}}</td>
</tr>
</tbody>

Categories

Resources