Filipe Picoito
Filipe Picoito

Reputation: 695

KO computed array filter not working

I've been learning KO but only know have I really took my time to create a small project with it from scratch.

I'm having trouble comprehending my issue, I'll briefly explain the logic:

You have pages, and those pages can have pages (children).

/* Defines a Page */
var page = function(ID, alias, body, connections) {
this.ID = ko.observable(ID);
this.alias = ko.observable(alias);
this.body = ko.observable(body);
this.connections = ko.observableArray(connections);
};

var dummyPages = [
new page(1, 'Alias #1', 'Body #1', [5]),
new page(2, 'Alias #2', 'Body #2', []),
new page(3, 'Alias #3', 'Body #3', [2,3]),
new page(4, 'Alias #4', 'Body #4', [5]),
new page(5, 'Alias #5', 'Body #5', [6]),
new page(6, 'Alias #6', 'Body #6', [7]),
new page(7, 'Alias #7', 'Body #7', [1, 4])
];

What I'm trying to do here is use a computed function what will be placed on a foreach html element.

<tbody data-bind="foreach: foreachConnectionAliases" style="text-align: left">

What it will do is basically get the children info of a page.

Here's my code:

/* Return the pages that are connected to the clicked one */
this.foreachConnectionAliases = ko.computed(function(){
alert(this.chPages);
if(typeof this.chPages === "undefined")
  return null;

var filtered =  ko.utils.arrayFilter(this.chPages(), function(page) {
                      return this.filterByThisPage(page.ID());
                  });
                  debugger;
return filtered;
}).bind(this);//, { deferEvaluation: true });

/* Finds out if the activeConnection exists for the page we want */
this.filterByThisPage = function(pageID){
alert('filterByThisPage');
ko.utils.arrayForEach(getPageConnections(), function(id) {
  if(id === pageID) return true;
});
return false;
}.bind(this);

/* Receive a page ID and get its connections array */
this.getPageConnections = function(){
alert('getPageConnections');
var getConnections = ko.utils.arrayFirst(this.chPages(), function(page){
                      return this.activeConnection() === page.ID();
                     });

return getConnections.connections();
}

Somewhere along the road I've placed many alerts as you can see. The alert in the computed function only fires once and I think its due to the return null.

An important thing to notice is that supposively I want this to fire as we change the value of this.activeConnection() which is an observable variable that holds the ID of the page we clicked on (to see the children).

Any ideas on fixing the code as well as in making it more clean is very appreciated as I believe I am over complicating this.

EDIT:

I also tried deferEvaluation: true and in that case I always get a undefined error on my foreach data-binding.

EDIT 2:

http://jsfiddle.net/g1q99jr8/6/

This briefly explains my situation. There might be a few mistakes on the HTML part.

Upvotes: 0

Views: 529

Answers (2)

super cool
super cool

Reputation: 6045

well you are trying to filter out based on connection ID which is entered in textbox based on click function .

First point to note here is there is no need to use a computed here click function might me more than enough and can be done in very simple way

View Model :

    var viewModel = function () {
    var self = this;
    var page = function (ID, alias, body, connections) {
        this.ID = ko.observable(ID);
        this.alias = ko.observable(alias);
        this.body = ko.observable(body);
        this.connections = ko.observableArray(connections);
    };

    var dummyPages = [
    new page(1, 'Alias #1', 'Body #1', [5]),
    new page(2, 'Alias #2', 'Body #2', []),
    new page(3, 'Alias #3', 'Body #3', [2, 3]),
    new page(4, 'Alias #4', 'Body #4', [5]),
    new page(5, 'Alias #5', 'Body #5', [6]),
    new page(6, 'Alias #6', 'Body #6', [7]),
    new page(7, 'Alias #7', 'Body #7', [1, 4])];

    self.chPages = ko.observableArray(dummyPages);
    self.activeConnection = ko.observable();
    self.foreachConnectionAliases = ko.observableArray();

    /* Return the pages that are connected to the clicked one */

    self.changeActiveConnection = function () {
        self.foreachConnectionAliases([]);
        var conValue = self.activeConnection();

        if (typeof self.chPages === "undefined") return null;

        var filter = ko.utils.arrayFirst(self.chPages(), function (page) {
            if (page.ID() == conValue) return true;
            else return false;
        });

        if(! filter) return true; 

        ko.utils.arrayForEach(filter.connections(), function (id) {
           ko.utils.arrayFilter(self.chPages(), function (page) {
                if (page.ID() == id) {
                    self.foreachConnectionAliases.push(page);
                    return true;
                } else {
                    return false;
                }
            });
        });
    }

};
ko.applyBindings(new viewModel());

working fiddle here

Any issue just in case let us know

Upvotes: 1

Knelis
Knelis

Reputation: 7149

Using the Fiddle you provided, I've changed your code to achieve what I think you want to achieve. For array searching, I've used jQuery.

Here's the HTML:

<input type="text" placeholder="Write a number, please" id="number" />
<button type="submit" data-bind="click: changeActiveConnection">
    Click me!
</button>
<div style="margin-bottom: 10px"></div>
<div data-bind="foreach:foreachConnectionAliases">
    <input type="text" data-bind="value:alias" />
    <br />
</div>

And JavaScript:

var viewModel = function () {
    var self = this;

    var page = function (ID, alias, body, connections) {
        this.ID = ko.observable(ID);
        this.alias = ko.observable(alias);
        this.body = ko.observable(body);
        this.connections = ko.observableArray(connections);
    };

    var dummyPages = [
        new page(1, 'Alias #1', 'Body #1', [5]),
        new page(2, 'Alias #2', 'Body #2', []),
        new page(3, 'Alias #3', 'Body #3', [2, 3]),
        new page(4, 'Alias #4', 'Body #4', [5]),
        new page(5, 'Alias #5', 'Body #5', [6]),
        new page(6, 'Alias #6', 'Body #6', [7]),
        new page(7, 'Alias #7', 'Body #7', [1, 4])];

    self.chPages = ko.observableArray(dummyPages);
    self.activePage = ko.observable();

    self.changeActiveConnection = function() {
        var number = +$('#number').val();

        // Find the selected page
        var match = $.grep(self.chPages(), function (e, i) {
           return e.ID() == number;
        });

        if (match.length < 1) // No element found
            return;

        self.activePage(match[0]);
    };

    /* Return the pages that are connected to the clicked one */
    self.foreachConnectionAliases = ko.computed(function() {
        var _page = self.activePage();

        if (_page == null || _page.connections().length < 1)
            return [];

        var aliases = [];

        // Loop through the connections of the current page
        _page.connections().forEach(function (e, i) {
            // Find the page again
            var connection = $.grep(self.chPages(), function (el, ix) {
                return el.ID() == e;          
            })[0];

            aliases.push(connection);
        });

        return aliases;
    });
};

ko.applyBindings(new viewModel());

And a working Fiddle.

Upvotes: 1

Related Questions