OfirD
OfirD

Reputation: 10460

How to make the visible binding to work

I'm experimenting with Knockout.js, and can't make the visible binding to work in the following code when returning an empty array (here's its fiddle):

$(document).ready()
{
    BuildFactoryGrid();
}

function BuildFactoryGrid ()
{
    var factoryViewModel = new FactoryViewModel();
    ko.applyBindings(factoryViewModel);
    factoryViewModel.init();
}

function FactoryViewModel ()
{
    var self = this;
    self.FactoryRecords = ko.observableArray([]);
    self.FactoryRecordsLength = ko.computed(function ()
    {
        return self.FactoryRecords().length;
    });

    self.init = function ()
    {
        self.FactoryRecords(GetFactoryData());
    };
}

function GetFactoryData ()
{
    var objArr =   
    [
      {
        District: 1, 
        Department: 22,
        Team: 33
      },
      {
        District: 1, 
        Department: 24,
        Team: 35
      },
      {
        District: 2, 
        Department: 54,
        Team: 9
      },
    ];
    return objArr;
    // return []; // remove comment to return an empty array
}
tr>th, td {
  text-align: center;
  vertical-align: middle;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>

<table class="table table-bordered table-condensed hover">
    <thead>
        <tr class="active">
            <th></th>
            <th>District</th>
            <th>Department</th>
            <th>Team</th>
        </tr>
    </thead>
    <tbody data-bind="foreach: FactoryRecords">
        <tr> 
            <td>
                <input type="checkbox" />
            </td>
            <td data-bind="text: District"></td>
            <td data-bind="text: Department"></td>
            <td data-bind="text: Team"></td>
        </tr>
        <tr data-bind="visible: $parent.FactoryRecordsLength() == 0 ">
            <td colspan="4">No Records</td>
        </tr>
    </tbody>
</table>

When returning an empty array, the "No Records" span should appear, but it doesn't. Be glad to understand why.

Upvotes: 1

Views: 61

Answers (3)

mykhailo.romaniuk
mykhailo.romaniuk

Reputation: 1088

Your visible binding "doesn't work", because you using it inside foreach. And since your FactoryRecords that binded to foreach have no element - no elements won't be generated and your visible binding won't be ever called.

Just fix you markup and separate foreach and default row:

<table class="table table-bordered table-condensed hover">
  <thead>
    <tr class="active">
      <th></th>
      <th>District</th>
      <th>Department</th>
      <th>Team</th>
    </tr>
  </thead>
  <tbody>
    <!-- Foreach generate elements if they are exists -->
    <!-- ko foreach: FactoryRecords -->
    <tr> 
      <td>
        <input type="checkbox" />
      </td>
      <td data-bind="text: District"></td>
      <td data-bind="text: Department"></td>
      <td data-bind="text: Team"></td>
    </tr>
    <!-- /ko -->
    <!-- If no elements present - create our row with message -->
    <tr data-bind="visible: FactoryRecordsLength() === 0 ">
      <td colspan="4">No Records</td>
    </tr>
  </tbody>
</table>

Upvotes: 1

Jason Spake
Jason Spake

Reputation: 4304

Your "No Records" block is nested within a foreach binding, which will not be drawn if there are no records to loop over. You'll have to move the "No Records" out of the foreach binding context, and to do that you'll want to move your foreach to a virtual element instead of the <tbody> element.

<tbody>
    <tr data-bind="visible: FactoryRecordsLength() == 0 ">
      <td colspan="4">No Records</td>
    </tr>
  <!--ko foreach: FactoryRecords-->
    <tr> 
      <td>
        <input type="checkbox" />
      </td>
      <td data-bind="text: District"></td>
      <td data-bind="text: Department"></td>
      <td data-bind="text: Team"></td>
    </tr>
    <!--/ko-->
  </tbody>

Upvotes: 1

John Pavek
John Pavek

Reputation: 2665

If you do

return objArr;
// return []; // remove comment to return an empty array

The array will be populated and FactoryRecordsLength().length will be 3

If you do

//return objArr;
 return []; // remove comment to return an empty array

The foreach binding will have nothing to display and you will not see anything in your table at all.

Ideally you should add another record to your object to determine if there are records, for example:

var objArr =   
[
  {
    District: 1, 
    Department: 22,
    Team: 33,
    Records: 0
  },
  {
    District: 1, 
    Department: 24,
    Team: 35,
    Records: 1
  },
  {
    District: 2, 
    Department: 54,
    Team: 9,
    Records: 0
  },
];

Upvotes: 0

Related Questions