Sort by characteristics of embedded array - Knockout

I'm struggling with filtering a nested array using Knockout.

My goal is to filter the array by both 'tag-names' and 'name'.

For example, searching for 'whal' should display all objects that contain a tag with that name in the grid.

Here is the Fiddle link: https://jsfiddle.net/king_s/fj9L3wjz/3/

<input data-bind="value: searchQuery, valueUpdate: 'afterkeydown'" />

<div data-bind="foreach: filter" class="grid">
    <p data-bind="text: name"></p>
</div>
(function(){
    var ViewModel = function() {
        var self = this;
        var obj = { "id": 11, 
            "name": "mouse", 
            "tags": [{ "id": 1, 
                "name": "critter"
            }] 
        };
        var obj2 = { "id": 12, 
            "name": "dolphin", 
            "tags": [{ "id": 2,
                "name": "fish"
            }, 
            {"id": 3,
                "name": "whale"
            }] 
        };
        self.searchQuery = ko.observable("");
        self.array = ko.observableArray([obj, obj2]);
        self.filter = ko.computed(function() {
            if(!self.searchQuery()) {
                return self.array();
            } else {
                return ko.utils.arrayFilter(self.array(), function(obj) {
                    return obj.name.toLowerCase().indexOf(self.searchQuery().toLowerCase()) > -1;
                });
            }
        });
    };
    ko.applyBindings(new ViewModel());
})();

Answer №1

To enhance the search functionality, consider adding a secondary check for the tags:

return ko.utils.arrayFilter(self.array(), function(obj) {
            var tagsCheck = ko.utils.arrayFilter(obj.tags, function(tag) {
            return tag['name'].toLowerCase().indexOf(self.searchQuery().toLowerCase()) > -1});

                return (obj.name.toLowerCase().indexOf(self.searchQuery().toLowerCase()) > -1) || tagsCheck.length;
                });

For further reference, take a look at this example

Answer №2

To iterate through your sub objects, follow this method:

self.filter = ko.computed(function() {
  if (!self.searchQuery()) {
    return self.array();
  } else {
    return ko.utils.arrayFilter(self.array(), function(obj) {
      var value = obj.name.toLowerCase().indexOf(self.searchQuery().toLowerCase());
      var compensation = 0; // Adjust for number of tags
      obj.tags.forEach(function (tag) {
        compensation--;
        value += tag.name.toLowerCase().indexOf(self.searchQuery().toLowerCase());
      })
      // Final value should be greater than -1 + (-1 * number of tags)
      return value > -1 + compensation;
    });
  }
});

Experiment with it here

UPDATE

I have revised my approach to make the code more concise:

self.filter = ko.computed(function() {
  if (!self.searchQuery()) {
    return self.array();
  } else {
    return ko.utils.arrayFilter(self.array(), function(obj) {
      var found = obj.name.toLowerCase().indexOf(self.searchQuery().toLowerCase()) > -1;
      obj.tags.forEach(function(tag) {
        if (tag.name.toLowerCase().indexOf(self.searchQuery().toLowerCase()) > -1) {
          found = true;
        }
      });
      return found;
    });
  }
});

Test it out on this fiddle

Answer №3

A while back, I developed a KO filters extender that proves to be useful for tasks like these. This extender creates a new property called filtered on the observableArray, providing a flexible way to apply multiple filters.

To implement this in your HTML, you would use:

<div data-bind="foreach: array.filtered" class="grid">

In your JavaScript code:

self.array = ko.observableArray([obj, obj2]).extend({filters: {
  search: function(obj) {
    var query     = self.searchQuery().toLowerCase(),
        nameMatch = obj.name.toLowerCase().match(query),
        tagMatch  = false;
    obj.tags.forEach(function(tag, i) {
      if (tag.name.toLowerCase().match(query))
        tagMatch = true;
    });
    return nameMatch || tagMatch;
  }
}});    

self.array.toggleFilter('search', true);

For an example, refer to the updated fiddle here.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The data type 'string[]' cannot be assigned to the data type 'listData[]'

I'm currently developing a flexible component that allows the list view to be utilized by different components. However, the challenge arises from the fact that each component has a different data format. In my project, I'm unable to use type any ...

Violation of Invariant: Incorrect hook usage or a cross-origin issue has occurred

After successfully getting my function to work, I decided to implement a loop for feedback from the backend post SSR. Wanting to utilize hooks, I converted it into a functional component and began writing code. However, even with an empty hook, I encounter ...

Encountering a 'type error' stating that $(...).modal is not a valid function

While working on a REACT project, I encountered an issue while trying to create and edit a function for updating notes by users using bootstrap modals. The error message that appeared is: Uncaught (in promise) TypeError: jquery__WEBPACK_IMPORTED_MODULE_1__ ...

The function grunt.task.run() is malfunctioning

Currently, I am experimenting with integrating Grunt into my Express application. Here is a snippet of the code I have: var grunt = require('grunt'); require(process.cwd() + '/gruntfile.js')(grunt); grunt.task.run('development&ap ...

FilterTextBoxExtender in AJAX enables the input of carriage returns

I need assistance with a multi-line text box that I'm using an AJAX FilteredTextBoxExtender on to restrict user input to just numbers. However, I also want users to be able to add a new line by pressing the enter key. I've looked around for a sol ...

Forwarding requests via middleware in next.js 13 AppDir: true

I am currently working on implementing a redirect feature in next.js 13 when the jwt back-end is not received. This is being done with the appdir activated. This is the code in my middleware.ts file: import { NextResponse } from 'next/server' im ...

Understanding Semantic Versioning (Semver) - A guide to properly Semvering major functional enhancements while maintaining backwards compatibility

It is my understanding that when using X.Y.Z, X is only changed for breaking updates while Y is reserved for backward compatible functional modifications. Therefore, can I infer correctly that even if my update involves a significant enhancement to functi ...

Encountering a problem where updating the selected option in a dropdown does not properly refresh the HTML

I am currently working with a dropdown menu containing countries. Initially, the selected item is set to Ukraine. <select id="country" name="country"> <option value="US">USA</option> <option value="UG">Uganda</option> ...

display or conceal a div when a radio button is selected

I need a way to display a specific div containing unique text once a radio button is selected, while hiding all other div sections. <?php foreach ($items as $item){ $i=1; echo ' <div class="form-check"> <input class="fo ...

"Nested AngularJS controllers: a deep dive into the world

Recently, I've been delving into the world of AngularJS and I can't shake the feeling that my approach to the paradigm might be a bit off. I have a controller for managing panes (linked to an ng-repeat) which tracks which panes the user has open ...

Incorporating JSON information into highcharts for advanced data visualization

If I were to utilize JSON code from a specific website, such as this one: "", in order to incorporate it into my highcharts implementation. The data seems to consist of x_min, x_max, and price_data values. What would be the best approach for creating hig ...

Utilizing JQuery and Jade to extract and display information from a Node.js server

I am currently working with the Jade framework for frontend and using Node.js & express for backend development. When rendering a view with data, I am encountering an issue where I can't access this data using JQuery. Below is my service in Node.js ...

Retrieve storage information when an internet connection is unavailable on React Native

One of my recent projects involves creating an application that retrieves data from a URL in the form of an array of objects and then displays this data using FlatList. Upon launching the application, the data is displayed correctly since it's retriev ...

The preflight request for OPTIONS is receiving a 400 bad request error on the secure HTTPS

When making an Ajax call on the front end and calling a WCF service through Ajax, I encountered an issue with adding headers. As a result, a preflight OPTIONS request is triggered but fails due to the URL being blocked by CORS policy. I tried adding the f ...

When working with data in Angular, make sure to use any[] instead of any in app.component.html and app.component.ts to avoid causing overload errors

I'm new to working with Angular, specifically using Angular 15. I have a Rest API response that I need to parse and display in the UI using Angular. To achieve this, I employed the use of HttpClient for making GET requests and parsing the responses. ...

Transform an item into a map of the item's properties

I am faced with an object containing unknown key/value pairs in this format: myObj = { key_1: value_1, key_2: value_2, key_n: value_n } My goal is to transform it into a dictionary of structured objects like the one below: dictOfStructureObjec ...

Tips for switching the status of each item as I navigate the page with the tab key

I am facing an issue with my list of items that have hidden content appearing on hover. I want to achieve the same functionality while tabbing through my page, but currently, all hidden content appears at once. Check out a live example of my code here jQ ...

Translate unknown provider in Angular using $translateProvider

Here is the situation I am facing: I am working on an Ionic project and I want to implement internationalization using angular-translate. To achieve this, I have added angular-translate.min.js to my project: <script src="lib/ionic/js/ionic.bundle.js"&g ...

Trouble with Ruby on Rails jQuery interactions when using the tokenInput gem

Currently, I am developing a rails app and attempting to incorporate this gem for a text field https://github.com/exAspArk/rails-jquery-tokeninput Despite having the gem correctly installed and included in my _form.html.erb file, I keep encountering the ...

What is the best way to stack several elements on top of each other?

<div class="parent"> <div class="child" id="child-A"> <div class="child" id="child-B"> <div class="child" id="child-C"> </div> The main concept here ...