0

Here is the code I am working with. The console log is showing me the onFilterChange method is being called twice (once for each function definition in the "select tags). I'm guessing I need to change "event: { change: onFilterChange }" in the data-bind, but I'm not familiar with Knockout.js, and we don't support ES6, so no using "()=>onFilterChange" which has worked for me in the past:

function ProductionsView() {
    var self = this;

    self.showDateFilter = ko.observable(true);
    self.showCategoryFilter = ko.observable(true);
    self.showDateFilter = ko.observable(true);

    self.onFilterChange = function(data,event) {
        if (self.init()) {
            self.doFilter();
            console.log("onFilterChange was called. This is happening twice on page load before interacting with the filter. ");
        }
    };


    self.doFilter = function() {
        var df = self.selectedDateFilter();
        var cf = self.selectedCatFilter();

        if (typeof cf == 'undefined' || typeof df == 'undefined') {
            return;
        }
        // these are all defined as observables too, but not important since it's the "onFilterChange" that I don't want to call 
        self.amFiltering("All" !== df || "All" !== cf);
        self.allCatAndDate("All" === df && "All" === cf);
        self.offset(0);
        self.doAjaxSearchFiltered(true);
    };
}

var pv = new ProductionsView();
ko.applyBindings(pv);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<div data-bind="visible: showDateFilterBar()">
  <select data-bind="
    value: selectedCatFilter,
    event: { change: onFilterChange },
    visible: showCategoryFilter()
  ">
    <option value="all">all</option>
    <option value="cat1">cat1</option>
  </select>
  <select data-bind="
    value: selectedDateFilter,
    event: { change: onFilterChange },
    visible: showDateFilter()
  ">
    <option value="all">all</option>
    <option value="date1">date1</option>
  </select>
</div>
Tomalak
  • 332,285
  • 67
  • 532
  • 628
baumannalexj
  • 766
  • 1
  • 9
  • 20
  • What is selectedCatFilter and selectedDateFilter, observable or computed? Can you show the code for those? – Jason Spake Jan 28 '17 at 06:46
  • If you don't want it to run when the page loads, when do you want it to run? Could you just wait to call `applyBindings()` until that event happens, and use that event to trigger the call? – coralvanda Jan 28 '17 at 09:00
  • @JasonSpake sorry, I added those observable declarations in. @coralv is this good documentation for using `applyBindings()` : http://stackoverflow.com/questions/18990244/whats-the-applybindings-second-parameter-used-for I would only like the method to be called `onchange` when selecting which filter one would like. Would I add `applyBindings(self, document.getElementById("date-results") )`? – baumannalexj Jan 28 '17 at 17:22

2 Answers2

0

I'm guessing that it is the applyBindings itself triggering the change events when your initial values get passed to the select elements.

You should subscribe to the change event on the observables themselves in your model rather than the event binding in the mark-up.

self.selectedCatFilter.subscribe(onFilterChange);

self.selectedDateFilter.subscribe(onFilterChange);
Jason Spake
  • 4,293
  • 2
  • 14
  • 22
0

Yes, you are using Knockout wrong. Knockout is all about automatic value dependencies, typically you don't need to set up any "change" event handlers at all.

You set up observables and subscribe to them. You will be notified when they change, for example when a bound UI element gets changed by the user, or when one of their dependencies changes.

To subscribe to observable values, simply use them inside a computed or .subscribe() to them directly. Both is shown below. Run the code sample to see it in action.

function ProductionsView() {
    var self = this;

    // values
    self.selectedDateFilter = ko.observable("all");
    self.selectedCatFilter = ko.observable("all");
  
    // computed values
    self.amFiltering = ko.computed(function () {
        return self.selectedDateFilter() !== "all" ||
               self.selectedCatFilter() !== "all";
    });
    self.allCatAndDate = ko.computed(function () {
        return self.selectedDateFilter() === "all" &&
               self.selectedCatFilter() === "all";
    });

    // subscriptions
    self.amFiltering.subscribe(function (value) {
        console.log("searching with filter=" + value);
        //self.doAjaxSearchFiltered(value);
    });
}

var pv = new ProductionsView();
ko.applyBindings(pv);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<select data-bind="value: selectedCatFilter">
  <option value="all">all</option>
  <option value="cat1">cat1</option>
</select>

<select data-bind="value: selectedDateFilter">
  <option value="all">all</option>
  <option value="date1">date1</option>
</select>

Viewmodel:
<pre data-bind="text: ko.toJSON($root, null, 2)"></pre>

You can go as far as creating a computed value that contains the Ajax parameters as an object and subscribe to that, like this:

self.searchParams = ko.computed(function () {
    return {
        date: self.selectedDateFilter(),
        cat: self.selectedCatFilter()
    };
});

self.searchParams.subscribe(function (params) {
    return $.get("/your/url", params).done(function (data) {
        // populate your observables with new data
    });
});

This way the Ajax request is auto-triggered on any relevant change.

Tomalak
  • 332,285
  • 67
  • 532
  • 628