If statement in knockout.js not working as expected - javascript

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.

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');
})

Nothing being displayed in place of AngularJS expression

I am trying to fetch data from mysql using php and trying to pass the data in json format to angularjs so that I can display data in table.
HTML code is:
<body ng-app="myModule">
<div class="row">
<div class="col-lg-12 text-center">
<div ng-controller="myController">
<table class="table">
<thead>
<tr>
<th>email</th>
<th>id</th>
<th>name</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="employee in employees"></tr>
<td>{{employee.username}}</td>
<td>{{employee.id}}</td>
<td>{{employee.name}}</td>
</tbody>
</table>
</div>
</div>
</div>
<!-- /.row -->
</div>
AngularJS code is:
var app = angular
.module("myModule", [])
.controller("myController", function ($scope,$http) {
$http.post('http://enrolin.in/test/login.php')
.then(function(response){
$scope.employees=response.data;
}); });
Working php link that outputs json is : http://enrolin.in/test/login.php
Working link of table is http://enrolin.in/test/
But when I try to load the html. It does not load any data from the database.
I tried to view it in console, looks like ng-repeat is repeated 6 times that is exactly the number of rows in database that means data is imported but is not being displayed somehow
It is just a silly mistake in your view (can't believe I overlooked it at first).
You are just repeating empty rows now:
<tbody>
<tr ng-repeat="employee in employees"></tr>
<td>{{employee.username}}</td>
<td>{{employee.id}}</td>
<td>{{employee.name}}</td>
</tbody>
Those tds obviously need to be inside the repeated row:
<tbody>
<tr ng-repeat="employee in employees">
<td>{{employee.username}}</td>
<td>{{employee.id}}</td>
<td>{{employee.name}}</td>
</tr>
</tbody>
The problem seems to be in your HTML.
<tr ng-repeat="employee in employees"></tr>
<td>{{employee.username}}</td>
<td>{{employee.id}}</td>
<td>{{employee.name}}</td>
The <td> are outside the <tr>, so {{ employee }} does not exist in the <td>.
This should work :
<tr ng-repeat="employee in employees">
<td>{{employee.username}}</td>
<td>{{employee.id}}</td>
<td>{{employee.name}}</td>
</tr>

Knockout: can't do conditional output within foreach block

I have an array with X elements. I am looping over the array, and when I get to the last element in the array, I want to output an additional column
<tr data-bind="foreach: columns">
<th>{{ label }}</th>
<!-- ko if: ($parent.columns.length - 1) == $index -->
<th>foo</th>
<!-- /ko -->
</tr>
It is not rendering the final column.
when you want to compute any observable variable in view by javascript you need to use parentheses.
View :
<table>
<thead >
<tr data-bind="foreach: columns">
<th data-bind="text:label"></th>
<th data-bind="if:$index() == $parent.columns().length-1">Hello</th>
</tr>
</thead>
</table>
Example : http://jsfiddle.net/GSvnh/5111/

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