Knockout Js - Using with inside if condition - javascript

The isAdmin property inside foreach is related in authorization class.
I am getting an error saying isAdmin is not defined. Is it because I am using isAdmin in foreach? How can I use a use property in with in foreach.
<div data-bind="with: authorization">
<table>
<tbody data-bind="foreach: users">
<tr>
<td data-bind="text: id"></td>
<td data-bind="text: name"></td>
<!-- ko if: isAdmin -->
<td><a href="#" class="icon-trash" rel="tooltip" title="Delete" ></a></td>
<!-- /ko -->
</tr>
</tbody>
</table>

You are correct, it is not working because the foreach binding gets its own context. Use $parent to access the parent authorization context.
<!-- ko if: $parent.isAdmin -->
<td><a href="#" class="icon-trash" rel="tooltip" title="Delete" ></a></td>
<!-- /ko -->

Related

Using knockout if binding inside a table

I was trying to use the if binding inside a table and the 3rd column is not at all displaying the text where as the entries in the 1st column are getting displayed.
<table id="searchPanelForm" cellspacing="0" cellpadding="0" align="center" class="conttable" width="100%" border="0" data-bind="">
<tbody>
<!-- ko foreach: searchPanelArray -->
<!-- ko if: $parent.isSearchEven($data.id) -->
<tr>
<td class="col2" data-bind="text: $data.label"></td>
<td class="col3"><input type="text"></td>
<!-- /ko -->
<!-- ko if: !$parent.isSearchEven($data.id) -->
<td class="col2" data-bind="text: $data.label"></td>
<td class="col3"><input type="text"></td>
</tr>
<!-- /ko -->
<!-- /ko -->
</tbody>
</table>
searchPanelArray is populated through an ajax call and isSearchEven returns true or false when the index is even or odd respectively.
Knockout expects "containerless control flow syntax" to act like elements. They cannot start on the outside of an element and end inside the element. So from Knockout's point of view, the comments internal to <tr> are simply malformed and ignored. So this is what it looks like for Knockout:
<!-- ko foreach: searchPanelArray -->
<!-- ko if: $parent.isSearchEven($data.id) -->
<tr>
<td class="col2" data-bind="text: $data.label"></td>
<td class="col3"><input type="text"></td>
<td class="col2" data-bind="text: $data.label"></td>
<td class="col3"><input type="text"></td>
</tr>
<!-- /ko -->
<!-- /ko -->
Logically, you want to group every two items in your array. You should create a computed observable the returns a new array with the two items grouped together.
this.searchPanelArrayGrouped = ko.pureComputed(function() {
var result = [], source = this.searchPanelArray();
for (var i = 0; i < source.length; i += 2) {
if (i + 1 >= source.length) {
result.push({left: source[i], right: {}});
} else {
result.push({left: source[i], right: source[i+1]});
}
}
return result;
}, this);
Html:
<!-- ko foreach: searchPanelArrayGrouped -->
<tr>
<td class="col2" data-bind="text: $data.left.label"></td>
<td class="col3"><input type="text"></td>
<td class="col2" data-bind="text: $data.right.label"></td>
<td class="col3"><input type="text"></td>
</tr>
<!-- /ko -->
https://jsfiddle.net/bg75xvxc/
Here is a related answer: https://stackoverflow.com/a/10577599/1287183

Collapsible data row with dynamic values

Context: There are a set of months in a table (e.g. May, Jun, July) and under those months will be all of the readings for the given month.
The code:
<tbody data-bind="visible: !MeterReadingHistory_IsBusy(), foreach: HeaderLines()" style="display: none">
<tr data-toggle="collapse" data-target=".order1">
<td>
<!-- ko if: meterReadings.length > 0-->
<span class="glyphicon glyphicon-chevron-down"></span>
<span class="reading-history-data" data-bind="html: monthName"></span>
<!-- /ko -->
<!-- ko if: meterReadings.length == 0-->
<span class="reading-history-data" data-bind="html: monthName"></span>
<!-- /ko -->
</td>
<td>
<span class="reading-history-data" data-bind="html: latestReadingDate"></span>
</td>
<td>
<span class="reading-history-data" data-bind="html: latestReadingType"></span>
</td>
<td>
<span class="reading-history-data" data-bind="html: latestReadingElectric"></span>
</td>
<td>
<span class="reading-history-data" data-bind="html: latestReadingGas"></span>
</td>
</tr>
<!-- ko if: meterReadings.length > 0 -->
<tr class="collapse order1" >
<td colspan="5">
<table class="table mb-none desktop-only">
<thead>
<tr>
<th>Day</th>
<th>Reading Source</th>
<th>Electricity</th>
<th>Gas</th>
</tr>
</thead>
<tbody>
<!-- ko foreach: meterReadings -->
<tr>
<td data-bind="text: readingDateParsed"></td>
<td data-bind="html: readingType"></td>
<!-- ko foreach: readings -->
<td data-bind="html: reading"></td>
<!-- /ko -->
<!-- ko if: readings.length == 0 -->
<td></td>
<td></td>
<!-- /ko -->
<!-- ko if: readings.length == 1 -->
<td></td>
<!-- /ko -->
</tr>
<!-- /ko -->
</tbody>
</table>
</td>
</tr>
<!-- /ko -->
</tbody>
The problem: When i click on any of the rows, it expands every month row exposing all of the data when in fact i only want to display the sub-rows for the actual month that has been clicked.
The main problem is that this table is dynamic, and we wont know how many header rows will be produced, therefore it will be difficult to assign each row to a specific data source via the data-bind attribute.
So......how can I get this code to display only the row of data that I have clicked on, e.g. May, and display all the readings for this given month while all of the other header rows remain closed?
Thanks!
$([data-toggle="collapse"]).on('click',function(){
var thisCollapse = $(this).attr('data-target');
$(thisCollapse).slideToggle('slow');
})

If statement in knockout.js not working as expected

I'm trying to display the string "No data was found." inside a table row whenever the data array is empty, but it seems the message get always printed no matter what.
Reproduction online (ignoring the condition orders.length ==0)
What am I doing wrong?
<table>
<thead>
<tr>
<th>Truck</th>
<th>Pickup</th>
</tr>
</thead>
<!-- ko if: orders.length==2 -->
<tbody>
<tr colspan="2">No data was found.</tr>
</tbody>
<!-- /ko -->
<tbody data-bind="foreach: orders">
<tr>
<td data-bind="text: truck"></td>
<td></td>
</tr>
</tbody>
</table>
If your orders is an ko.observableArray you need to write orders() to get the underlying array and get the length from there:
<!-- ko if: orders().length== 0 -->
Your HTML also invalid, the td elements are missing from:
<!-- ko if: orders().length==0 -->
<tbody data-bind="if: orders().length==0">
<tr colspan="2"><td>No data was found.</td></tr>
</tbody>
<!-- /ko -->
Demo JSFiddle.

Nested containerless foreach in tbody failing for IE

I'm having a bit of trouble getting some nested, containerless foreach bindings to work. It works in grownup browsers, but not IE (8 OR 9).
The closest I could find was this question, but the root of that problem seems to be a lack of a tbody tag, which I have. The error IE is giving is
Cannot find closing comment tag to match: ko foreach: seniors
Sorry for the wall of text, but below is my markup.
<tbody data-bind="foreach: superGroups">
<tr>
<td style="font-weight: bold;" data-bind="text: superName() || 'No Super Set'" colspan="8">
</tr>
<!-- ko foreach: seniors -->
<tr>
<td></td>
<td style="font-weight: bold;" data-bind="text: seniorName() || 'No Senior Set'" colspan="7"></td>
</tr>
<!-- ko foreach: items -->
<tr>
<td>
<span data-bindX="text:superName"></span>
</td>
<td>
<span data-bindX="text:seniorName"></span>
</td>
<td>
<span data-bind="text:clientName"></span>
<i class="icon-tags" data-bind="attr:{title: labels}, visible: labels"></i>
</td>
<td>
<span data-bind="text:description"></span>
</td>
<td>
<span data-bind="visible:superPayAmount">$<span data-bind="text:superPayAmount"></span></span>
<span data-bind="visible:superPayAmount.length == 0">-</span>
</td>
<td>
<span data-bind="shortDate: superStartDate"></span> - <span data-bind="shortDate: superEndDate"></span>
</td>
<td>
<span data-bind="visible:seniorPayAmount">$<span data-bind="text:seniorPayAmount"></span></span>
<span data-bind="visible:!seniorPayAmount.length == 0">-</span>
</td>
<td>
<span data-bind="shortDate: seniorStartDate"></span> - <span data-bind="shortDate: seniorEndDate"></span>
</td>
</tr>
<!-- /ko -->
<!-- /ko -->
</tbody>
You missed closing td tag in the first tr:
<tr>
<td style="font-weight: bold;" data-bind="text: superName() || 'No Super Set'" colspan="8"></td>
</tr>

Knockout.js -- understanding foreach and with

I've been going through the learn.knockout.js tutorials and been experimenting. Can someone explain why this works [Tutorial: Single page applications, Step 2] (using with: chosenFolderData and foreach: mails):
<!-- Mails grid -->
<table class="mails" data-bind="with: chosenFolderData">
<thead><tr><th>From</th><th>To</th><th>Subject</th><th>Date</th></tr></thead>
<tbody data-bind="foreach: mails">
<tr>
<td data-bind="text: from"></td>
<td data-bind="text: to"></td>
<td data-bind="text: subject"></td>
<td data-bind="text: date"></td>
</tr>
</tbody>
</table>
but not this (using just foreach: chosenFolderData.mails):
<!-- Mails grid -->
<table class="mails">
<thead><tr><th>From</th><th>To</th><th>Subject</th><th>Date</th></tr></thead>
<tbody data-bind="foreach: chosenFolderData.mails">
<tr>
<td data-bind="text: from"></td>
<td data-bind="text: to"></td>
<td data-bind="text: subject"></td>
<td data-bind="text: date"></td>
</tr>
</tbody>
</table>
I suspect it's because while chosenFolderData is observable, chosenFolderData.mails is not. Can anyone tell me for certain?
Many thanks!
-- Ralph
Because you are not actually accessing the property you want with the way it is written. In the model chosenFolderData is an observable and must be called like a method to retrieve the value. To provide the functionality without using with (and I suggest not using with where high performance is necessary because of the overhead)...
<tbody data-bind="foreach: chosenFolderData().mails">

Categories

Resources