Antonio Vasilev
Antonio Vasilev

Reputation: 2965

Knockout.js - observable within obserable

Searched all over stackoverflow & other sources - can't seem to find anything of much help.

I have a statistical table-like UI with data driven content. The data is a huge array/object serialised on the back-end.

I am then extracting the values within that array - which are set as observables - and outputting them on the page with data-bind attributes.

The table displays the top performing artist in a given category and the two related artists below.

The problem is that we need to dynamically change the data-bind attribute so that it is capable of looking at a variable property in the data.

For example, if the user filters by "Artist", we want to display the Artist's Name (i.e

<span data-bind="text: ArtistName()"></span>

BUT

if the user filters by "Genre", we want the same part of the HTML to be able to display the Track Length (or any other arbitrary observable property) (i.e )

Ultimately, I want to have a variable data-bind attribute, like

I know that I can achieve this using a potentially huge if statement, like so:

<!-- ko if: $parent.SortMethod() == 'Topic1' -->
<span data-bind="text: Topic1()"></span>
<!-- /ko -->
<!-- ko if: $parent.SortMethod() == 'Topic2' -->
<span data-bind="text: Topic2()"></span>
<!-- /ko -->
<!-- ko if: $parent.SortMethod() == 'Topic3' -->
<span data-bind="text: Topic3()"></span>
<!-- /ko -->

...etc

Surely there is a more efficient way...?

Upvotes: 2

Views: 133

Answers (1)

haim770
haim770

Reputation: 49095

You'll have to bind the grid data to a function that will then return the data sorted / filtered by the desired field. Also, you'll have to bind that drop-down list to an observable in your view-model.

function GridModel(data)
{
      var self = this;
      self.RawData = data;
      self.SortedBy = ko.observable();

      self.GetGridData = function()
      {
           var ret = self.RawData();
           var sortingFld = self.SortedBy();

           if (sortingFld)
           {
               ret = ret.sort(function (a, b) {
                   a = ko.unwrap(a[sortingFld]);
                   b = ko.unwrap(b[sortingFld]);

                   return (a == b ? 0 : a < b ? -1 : 1) * 1;
                   });
           }

           return ret;
      }
}

HTML:

<table class="grid">
    <thead>
        <th>Category</th>
        <th>Artist</th>
        <th>Track</th>
    </thead>

    <tbody data-bind="foreach: GetGridData()">
        <td data-bind="text: Category"></td>
        <td data-bind="text: Artist"></td>
        <td data-bind="text: Track"></td>
    </tbody>
</table>

The drop-down will then be able to change the SortedBy observable and automatically refresh the grid accordingly:

<select data-bind="value: SortedBy, options: ['Artist', 'Track']"></select>

(The code above only demonstrates ascending sorting, but you can easily expand it for filtering as well).

Upvotes: 2

Related Questions