4
votes

I have been struggling for a few days with knockout data binding. The simple yet very annoying example of problem that I struggle with is down below.

I have a simple ViewModel class with a method that changes boolean from false to true. Knockout does bind with HTML on page load, but there seems to be a problem with data bind on click event.

Observable does change when I debug the code, but View stays the same.

Whole code and (not)working example are down below.

function ViewModel (data) {
	
	var self = this;
	
	this.textFlag = ko.observable(false);
	
	this.changeText = function (eventID, panelStatus) {
		this.textFlag = ko.observable(false);		
		
		if(eventID == 1) this.textFlag(panelStatus);
	}
}

var viewModel = new ViewModel();
ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<h1 data-bind="text: textFlag() === true"></h1>
<button data-bind="click: changeText(1, true)">Button</button>

See example on jsfiddle

3
See Durga's edit, you can make your live example right here on site, instead of offsite in a fiddle, most of the time. - T.J. Crowder
Please explain what you want to do in a bigger context. It's obvious where your mistake is and what to do to resolve it, but that's still not using knockout properly. There likely is a better way to achieve what you want to do, if you explain what this is for. - Tomalak

3 Answers

2
votes

You are redefining this.textFlag variable, thus breaking the binding. All you have to do is update its value using this.textFlag(panelStatus); like this :

function ViewModel(data) {

  var self = this;

  this.textFlag = ko.observable(false);

  this.changeText = function(eventID, panelStatus) {
    if (eventID == 1) {
      this.textFlag(panelStatus);
    }
  }
}

var viewModel = new ViewModel();
ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<h1 data-bind="text: textFlag() === true"></h1>
<button data-bind="click: function () { changeText(1, true) }">Button</button>

As stated in TJ Crowder answer you also have to either wrap your call to your function inside another anonymous function as seen in docs or create another function using bind like suggested.

2
votes

Two issues:

  1. You're recreating your observable in changeText. You need to remove the this.textFlag = ko.observable(false); line. You've already done that bit.

  2. Your click handler is defined incorrectly. Remember that KO takes your bindings and effectively creates an object initializer out of them. Let's look at the one that would create:

    {
        click: changeText(1, true)
    }
    

    See the problem? That calls changeText(1, true) and assigns the result of the call to click. Instead, you want to provide a function reference, so click can call the function.

    (Obviously, what KO actually does is more complicated than that, with lots of with wrappers, but eventually that's what it does.)

Once you remove the errant line from changeText, you could do this:

<button data-bind="click: changeText.bind($data, 1, true)">Button</button>

Live Example:

function ViewModel (data) {
	
	var self = this;
	
	this.textFlag = ko.observable(false);
	
	this.changeText = function (eventID, panelStatus) {
   		
		if(eventID == 1) this.textFlag(panelStatus);
	}
  }

var viewModel = new ViewModel();
ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<h1 data-bind="text: textFlag() === true"></h1>
<button data-bind="click: changeText.bind($data, 1, true)">Button</button>

...but it may be better to define a function on the viewmodel that uses 1, true:

this.changeText1True = function() { return this.changeText(1, true); };

...and call that:

<button data-bind="click: changeText1True">Button</button>

(Obviously, use a better name.)

Live Example:

function ViewModel (data) {
	
	var self = this;
	
	this.textFlag = ko.observable(false);
	
	this.changeText = function (eventID, panelStatus) {
   		
		if(eventID == 1) this.textFlag(panelStatus);
	}
  
  this.changeText1True = function() {
    return this.changeText(1, true);
  }
}

var viewModel = new ViewModel();
ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<h1 data-bind="text: textFlag() === true"></h1>
<button data-bind="click: changeText1True">Button</button>
-1
votes

function ViewModel (data) {
	
	var self = this;
	
	self.textFlag = ko.observable(false);
	
	self.changeText = function (eventID, panelStatus) {
				
		if(eventID === 1) {
		 self.textFlag(panelStatus);
		}
	}
}

var viewModel = new ViewModel();
ko.applyBindings(viewModel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<h1 data-bind="text: textFlag"></h1>
<button data-bind="click: changeText.bind($data, 1, true)">Button</button>

Made some change to your code.