Modify the database entry only if the user manually changes it, or temporarily pause specific subscriptions if the value is altered programmatically

After a change in the viewmodel, I want to immediately update the value on the server.

class OrderLine
{
    itemCode: KnockoutObservable<string>;
    itemName: KnockoutObservable<string>;

    constructor(code: string, name: string)
    {
        this.itemCode = ko.observable(code);
        this.itemName = ko.observable(code);
        
        this.itemCode.subscribe(this.updateCode, this, "change");
        this.itemName.subscribe(this.updateName, this, "change");
    }

    updateCode = (newvalue: string) =>
    {
        //Update value on the server
    }

    updateName = (newvalue: string) =>
    {
        //Update value on the server
    }
}

Users can change both values, and with explicit subscriptions, updating to the server/database works correctly.

On the server side, updating itemCode will also update the value of itemName. Thus, the response to the client will include a json object with the new value for itemName

The issue arises when changing the value of itemName in the viewmodel triggers a subscription callback method that updates the same value on the server again

    updateCode = (newvalue: string) =>
    {
        //Update value on the server
        //Upon successful request
        this.itemName(updatedvaluefromServer);   
    }

Question: Is it possible to change the value of a KnockoutObservable that only notifies view subscribers?
Or is there a way to detect if the value was changed from the view?

I attempted to use the "sneaky update" technique by @RPNiemeyer mentioned here
However, this approach suspends notification for all subscribers, including those in the view

Answer №1

Check out this innovative approach that combines a computed observable with a regular observable:

class ProductItem
{
    private _itemNumber: KnockoutObservable<string>;
    private _itemName: KnockoutObservable<string>;
    itemNumber: KnockoutComputed<string>;
    itemName: KnockoutComputed<string>;

    constructor(number: string, name: string)
    {
        this._itemNumber = ko.observable(number);
        this._itemName = ko.observable(name);

        this.itemNumber = ko.computed({
            read: () => this._itemNumber(),
            write: (newVal) => {
                this._itemNumber(newVal);
                // Update database
                // Upon a successful response:
                this._itemName("...");
            }
        });

        this.itemName = ko.computed({
            read: () => this._itemName(),
            write: (newVal) => {
                this._itemName(newVal);
                // Update database
            }
        });
    }
}

Remember to update the underlying observable in the success callback of your AJAX requests, rather than directly in the write function of the computed property, to avoid any issues.

Answer №2

Let me explain the concept I mentioned earlier. The code is an observable value, while the name is a writable computed value. When the code gets updated, it automatically updates the read value of name. On the other hand, when you write to the name, it updates the code which in turn updates the read value of name. This results in only one update to the observable value of name, so there are not two separate updates.

If you observe the console carefully, you will see that updating either field triggers a single update for each of them.

function orderLine(code, name) {
  return {
    code: code,
    name: name
  };
}
var serverValues = [
  orderLine(1, 'one'),
  orderLine(2, 'two'),
  orderLine(3, 'three')
];

function getNameFromCode(code) {
  var found = ko.utils.arrayFilter(serverValues, function(line) {
    return line.code == code;
  });
  if (found.length == 0) return '';
  return found[0].name;
}

function getCodeFromName(name) {
  var found = ko.utils.arrayFilter(serverValues, function(line) {
    return line.name == name;
  });
  if (found.length == 0) return '';
  return found[0].code;
}

function vm() {
  var self = {};
  self.code = ko.observable();
  self.name = ko.computed({
    read: function() {
      return getNameFromCode(self.code());
    },
    write: function(newValue) {
      console.debug("Writing code");
      self.code(getCodeFromName(newValue));
    }
  });

  self.code.subscribe(function(newValue) {
    console.debug("Updating code to:", newValue);
  });
  self.name.subscribe(function(newValue) {
    console.debug("Updating name to:", newValue);
  });

  return self;
}

ko.applyBindings(vm());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<label>Code</label>
<input data-bind="value:code" />
<br />
<label>Name</label>
<input data-bind="value:name" />

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

Tips for implementing and utilizing onclick functions in EJS

My goal is to develop a trivia game with interactive features. I aim to enable users to click on an answer, which will trigger a border effect and increase the points variable. Below is the layout of the entire page: <% include ../partials/boilerp ...

Ajax script causes error 403 when loading content while scrolling

Currently in the process of creating a blog using the HubSpot platform. The primary goal is to have blog posts load dynamically as users scroll down the page. I came across a script that claims to achieve this functionality and is designed specifically for ...

Retrieve the nth element from an array using a function that requires 2 arguments

During my coding journey, I encountered a challenge that has proven to be quite tricky. The task in question goes as follows: Create a function that accepts an array (a) and a value (n) as parameters Identify and store every nth element from the array in ...

Transforming API response data into a Typescript object using React mapping

When I make an API call, the response data is structured like this: [ { "code": "AF", "name": "Afghanistan" }, { "code": "AX", "name": "Aland Islands" } ...

What could be causing me to receive no results?

Currently, I am expanding my knowledge in JavaScript, Ajax, and NodeJs. My current project involves creating a webpage that can display a string retrieved from the server. The server-side code is as follows: var express = require('express'); v ...

Close pop-up upon successful AJAX response in ASP.NET MVC

I have a modal in my view that allows me to create a new record in the database. The view for this functionality is contained within a partial view. Below is the code for the view: <script src="~/Scripts/jquery-3.1.1.js"></script> To han ...

What is the best way to assign unique IDs to automatically generated buttons in Angular?

Displayed below is a snippet of source code from an IONIC page dedicated to shapes and their information. Each shape on the page has two buttons associated with it: shape-properties-button and material-information-button. Is it possible to assign different ...

Is there a chance of a race condition occurring during file uploads when processed individually through AJAX with PHP?

I have created a form for uploading multiple files. <form id="myuploadform" enctype="multipart/form-data"> <input id="uploadedFiles" name="uploadedFiles" type="file" class="form-control&qu ...

Guide on importing npm packages without TypeScript definitions while still enabling IDE to provide intelligent code completion features

I am currently utilizing an npm package that lacks type definitions for TypeScript. Specifically, I'm working with the react-google-maps library. Following their recommended approach, I have imported the following components from the package: import ...

What is the best way to retrieve the specific property from a bound function?

I'm looking to retrieve the value of a specific property from a function that has already been bound. function foo(){ console.log(this.a) } const bar = foo.bind({a:1}) bar() // Outputs 1 bar.getThis() // expected result is { a: 1 } Given the code ...

Having trouble hiding the message "Not found" with ng-hide and filters in AngularJS

I am currently working on incorporating instant search functionality with filters in AngularJS. My goal is to have the "Not Found!!" message displayed when the filter results in an empty array, indicating that no matches were found. However, I have encou ...

Display or conceal a <div> segment based on the drop down selection made

A dropdown menu controls the visibility of certain div elements based on the selection made. While this functionality is working for one dropdown, it's not working for another even though the code is very similar. I've tried various solutions but ...

What is the most effective way to structure a React function incorporating nested objects and mapping?

As a newcomer to Typescript, I am facing challenges in properly typing the following code snippet. I have experimented with Interfaces and individually typing properties as well, but it seems like I am only scratching the surface and encountering new typin ...

AgGrid supports multi-line content within its cells

I attempted to utilize this solution, however, it is not functioning properly for me. While it does resize the column height correctly, the text is still not wrapped as expected. Ag-Grid - Row with multiline text let gridOptions = { columnDefs: column ...

Is there a way to modify page URLs without causing a refresh, regardless of browser?

Despite my extensive searches on various online platforms, including stackoverflow, I have yet to come across a satisfactory answer to my question. While I find the window.history.pushState() and window.history.replaceState() methods quite user-friendly, ...

How can we effectively manage error responses and retry a failed call in NodeJS without getting tangled in callback hell?

I am in search of an effective approach to handle the given situation. I am curious if employing promises would be a helpful solution? Situation Overview: When a call retrieves a callback, and this callback receives an error object as a parameter. My obj ...

Is it possible to populate a div using jQuery Datatable?

Seeking help with jQuery datatable as a newbie. How can I populate divs instead of tables with the data from an ajax call? The structure should resemble a table: <div id="wrap"> <div class="drow"> <div class="dco ...

Steps for transforming 112889 (in mmddyy format) into 11/28/89 or 11/28/1989

Is there a way to convert the unformatted date 112889 (mmddyy) into a specific format like 11/28/89? console.log(new Date('112889')) // The output I'm getting is: Sat Jan 01 112889 00:00:00 GMT+0800 I've searched extensively on Google ...

The ng-repeats functionality is triggered multiple times whenever I attempt to make this call

I have a situation where I am using ng-repeat in my Laravel view to call a function from the controller. This function retrieves data from the database, performs some calculations, and then returns an array. However, I am facing an issue where the data is ...

The update feature activates upon reaching the bottom of the page, but it continues to refresh constantly

In my VueJS component, I have implemented a scroll event that triggers an AJAX call to update the Jobs() function when the user is getting close to the end of the page. if ( windowScrollTop >= (documentHeight - windowHeight - 50) ) { this.updat ...