"Exploring the nuances of Knockout computed and subscriptions: a dive into timing complexities

Recently discovered an interesting behavior in KnockoutJS where subscription functions are evaluated before dependent computables. I'm looking for someone who can confirm this, as I haven't been able to find any information about the timing of Knockouts in the documentation or discussion forums.

This situation arises when working with a model structure like the one below...

var itemModel = function (i) {
    var self = this;

    self.Id = ko.observable(i.Id);
    self.Title = ko.observable(i.Title);
    self.State = ko.observable(i.State);

};

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

   self.Items = ko.observableArray() // <-- some code initializes an Array of itemModels here
   self.indexOfSelectedItem = ko.observable();

   self.selectedItem = ko.computed(function () {
       if (self.indexOfSelectedItem() === undefined) {
            return null;
       }
       return self.Items()[self.indexOfSelectedItem()];
   });
};

In this scenario, when subscribing to the index field like so...

appModel.indexOfSelectedItem.subscribe(function () {
    // Do something with appModel.selectedItem()
    alert(ko.toJSON(appModel.selectedItem()));
}

The subscription function is being executed before the computed observable is recalculated with the new index value. This leads to getting the selectedItem() corresponding to the last selected index instead of the current selected index.

I have two questions:

  • Is my understanding correct?
  • If so, why should I utilize ko.computed() when a simple function can give me the current selected item every time it is called? Especially considering that ko.computed gets evaluated after everything has already completed and may no longer be needed.

Answer №1

By default, Knockout evaluates all computeds eagerly instead of lazily (i.e., not upon first access).

When any dependency changes, all subscriptions are notified and connected computeds are re-evaluated. You can opt for a "lazy" evaluation by specifying the deferEvaluation option in a computed observable, though this cannot be applied to a subscription.

However, focusing on the index of the selected item may not be necessary. It is better design to focus on the actual item itself rather than its numerical position in an array.

To address this, you could create a writable computed observable that provides the index of the currently selected item for display purposes and allows for changing it conveniently.

function AppModel() {
    var self = this;

    self.Items = ko.observableArray();
    self.selectedItem = ko.observable();

    self.indexOfSelectedItem = ko.computed({
        read: function () {
            var i,
                allItems = self.Items(),
                selectedItem = self.selectedItem();

            for (i = 0; i < allItems.length; i++) {
                if (allItems[i] === selectedItem) {
                    return i;
                }
            }
            return -1;
        },
        write: function (i) {
            var allItems = self.Items();

            self.selectedItem(allItems[i]);
        }
    });
}

In Knockout, it is preferred to manage actual values rather than just indexes. Adjusting your view to reflect these changes should not pose significant challenges. Update any code that previously interacted with indexOfSelectedItem to now directly interact with selectedItem. Dependencies on selectedItem will function as usual.

In a well-structured Knockout application, the need to work with array item indexes is rare. Once everything is functioning correctly, consider removing the write portion of the computed observable.

View example here: http://jsfiddle.net/4hLLn/

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

Use JavaScript to retrieve a value and display it on a PHP page

I am trying to create a system that can generate and deliver JSON data. Here is the PHP code I have so far: <?php header("Content-Type:application/json"); require "data.php"; if(!empty($_GET['name'])) { $name=$_GET['name']; ...

Implement image uploading feature with Ant Design library in a React JS application

I need assistance with a basic application that allows users to upload images. Once the image is uploaded and the user clicks on the get data from upload button, the result should be displayed in the console as console.log("Received values of form: ", valu ...

How come submitting a form without refreshing does not update the database with new values?

I'm encountering an issue with my form and script that is supposed to connect to the operation.php class. Despite having everything set up correctly, the data is not being added to the database and the page does not refresh. I'm perplexed as to ...

Guidelines for transferring data when a button is held down or pressed

I am looking to continuously send values while a button is pressed. Currently, a value is only sent with each click. Below is the current code: my_custom_script.js $(document).ready(function() { $('#left').mousedown(function() { var left ...

Having trouble with loading a new page on an HTML website

My website is successfully updating the database, printing all values, but for some reason the new page is not opening. The current page just keeps refreshing and I'm receiving a status of 0. Below is my code snippet: try{ console.log("here1 "+e ...

Determining the optimal route to retrieve the key value in JSON format?

Here is a sample json data: var countryData = { "USA":{ "W": 97.0, "N":42.5, "E": 130.0, "S": 20.0, "vert_%": 170 }, }; While I know how to access the values: var myValue = countryData.USA.W; How can I access a specific key like W or USA? ...

managing the HTML class names and IDs for various functions such as styling, jQuery interactions, and Selenium automation

While there is an abundance of articles on writing clean HTML/CSS, one aspect that seems to be lacking advice is how to organize class names and IDs for different purposes such as design, jQuery, and Selenium testing. The challenge lies in deciphering the ...

Configuration of an MVC-based web application

As a newcomer to web application development, I am currently working on building a web application using the Model-View-Controller pattern. My setup includes a MySQL database for the Model, JSP pages for the Views, and a DAO for the Controller. I am looki ...

Is it possible to send the result to the browser without using result.send?

Although I can successfully query a MS-SQL server, I'm having trouble displaying the result in the browser. var express = require('express'); const sql = require("mssql/msnodesqlv8"); var app = express(); const main = async() => { ...

Are there any options for a JavaScript coding platform that can be used on a tablet device?

As someone who frequently travels by train, I recently purchased an Android tablet and have a strong desire to learn JavaScript. While I can read books on my tablet, I am eager to also be able to program on it. Are there any options available for develop ...

Is React dependent on the render process to update its state?

In my code, I am encountering an issue where the state of a key is not updating correctly even after performing operations on its value within a function. The scenario involves a function named clickMe, which is triggered by an onClick event for a button ...

Maintain Angular Dropdown Menu Open Across Page Refresh

I am currently working on an HTML/Typescript view that is connected to a SQL Database. Whenever there are changes made to the database, the entire webpage reloads. The issue we are facing is that we have dropdown menus on the page that clients want to rema ...

Experiencing a hiccup in your jQuery animation?

Click here to access the fiddle demonstrating the issue. A situation arises where a span with display: inline-block houses another span that is being slowly hidden. The container span unexpectedly shifts back to its original position once the hiding proces ...

Issue with npm version: 'find_dp0' is not a valid command

Hello, I have a small node application and encountered an issue while running a test. The error message displayed is as follows: 'find_dp0' is not recognized as an internal or external command, operable program or batch file. It seems to be re ...

During the second request, Ajax is unable to retrieve any data

Currently, I am working on a calendar project where I utilize an ajax request upon page load to fetch data from the rails database. Initially, the ajax call successfully retrieves the object during the page load event. However, when I attempt to retrieve ...

Initiate an Ajax request solely for the elements currently visible on the screen

I am currently facing an issue that requires a solution. Within our template, there are multiple divs generated with the same classes. Each div contains a hidden input field with the ID of the linked target site. My task is to utilize this ID to make aja ...

Struggles encountered when choosing the initial visible item

I have a set of 3 tabs, each with its own heading and content. However, I only want to display the tabs that the user selects by checking the corresponding checkboxes. There are 3 checkboxes, one for each tab. Below is the code snippet: //Function to ...

Determine the height of an element in JavaScript or jQuery by using the CSS property height: 0;

I'm facing a challenge in determining the actual height of an element, which currently has a CSS height property set to: height: 0; When I check the console, it shows a height of 0, but I'm looking to find the real height of the element. I als ...

What steps can be taken to resolve the vulnerability in webpack-pwa-manifest?

Looking for solutions to address the [email protected] and minimist vulnerability. I attempted removing node/modules and package-lock.json, followed by a fresh npm installation, but the issue persists. Any suggestions would be greatly appreciated. Scr ...

What is the best way to show the initial 20 words on the screen, followed by the next 20 words using

Within a single tag, I have a string as shown in the example below. My goal is to display the first 20-25 words on screen, then continue with the next set of words, and so forth. Once 20 words are displayed, the remaining text should be hidden. <p>Lo ...