Reputation: 696
Background: I am working on a webpage using Knockout 3.4.0. On the page, I use a computed observable to calculate the cost of a fee (in dollars). The fee can either be a percentage or a flat rate, and this percentage/flat rate information can be inserted into one of two textboxes. I use radio buttons and jQuery for the user to select which text box is active, as the user can only pick percentage or flat rate. I am also loading the view model using JSON and ko.mapping, but that doesn't seem to be the issue here. I should also mention that the client's target browser is IE11 or Edge.
Issue: The computed observable does not seem to be subscribing to changes in the flat rate text box. When I run the application on my localhost (the application itself is .NET MVC), the flat rate text box responds once. When I moved the code to JSFiddle for this write up, the box does not respond at all. Here is the observable:
self.serviceFeeComputed= ko.computed(function () {
if (self.ServiceFee.Percentage() !== null
&& self.ServiceFee.Percentage() !== 0) {
return self.Amount() * (self.ServiceFee.Percentage() / 100);
} else {
return self.ServiceFee.FlatRate();
}
});
Attempts at Resolution: I've spent around 6 - 8 hours over the last two weeks trying to fix this issue. Here are some of the things I've tried:
I've defined the view model in different fashions, such as a function, or JavaScript object: var viewModel = { ... }
I've turned ServiceFee into an observable - no results, so I changed back.
I've copied the Percentage textbox over the Flat Rate text box to ensure that the other attributes were the same.
I've switched from ko.computed()
to ko.pureComputed()
I've switched the order in which the conditional statement operates, so that I check self.ServiceFee.FlatRate()
first. This causes the flat rate textbox to update the fee, but not the percentage. Keying off this fact, I did a search and found this SO question, but upon calling both observables in the computed, I didn't get a working result. I also tried different variations of this solution, such as creating variables using the observables, or adding them together. Regardless of what I do, it seems that the first variable examined in the conditional statement is the one that receives the subscription.
self.serviceFeeComputed = ko.computed(function () {
self.ServiceFee.Percentage();
self.ServiceFee.FlatRate();
if (self.ServiceFee.Percentage() !== null && self.ServiceFee.Percentage() !== 0) {
return self.Amount() * (self.ServiceFee.Percentage() / 100);
} else {
return self.ServiceFee.FlatRate();
}
});
Ideal Resolution: I'd like for both the text boxes to update the computed observable, regardless of how many times the user switches back and forth between percentage and flat rate.
Thanks in advance.
Upvotes: 1
Views: 297
Reputation: 264
Your problem came from two things:
I have fixed your code at CodePen
// You will not need jQuery at all, KO is enough
// You need a boolean property to determine if the Flatrate or the Percentage is used
self.ServiceFee = {
Percentage: ko.observable(),
FlatRate: ko.observable(),
IsPercentage: ko.observable(true) // Add this property to ServiceFee
};
self.serviceFeeComputed = ko.computed(function() {
//if (self.ServiceFee.Percentage() !== null && self.ServiceFee.Percentage() !== 0) {
if (self.ServiceFee.IsPercentage()) { // this is the right condition
return self.Amount() * (self.ServiceFee.Percentage() / 100);
} else {
return self.ServiceFee.FlatRate();
}
});
<!-- You will not need jQuery at all, KO is enough -->
<!-- You need a boolean property to determine if the Flatrate or the Percentage is used -->
<label class="radio-inline col-xs-2 col-xs-offset-1">
<input name="Test" type="radio" data-bind="checkedValue: true, checked: ServiceFee.IsPercentage" />
<!-- <input type="radio" name="percentageOrFlatRate" value="percentage" /> -->
</label>
... other code ...
<input class="form-control"
data-bind="textInput: ServiceFee.Percentage, valueUpdate: 'afterkeydown', enable: ServiceFee.IsPercentage"
data-value="percentage" id="ServiceFee_Percentage" name="ServiceFee.Percentage"
placeholder="0.00" type="number" value="" />
... other code ...
<label class="radio-inline col-xs-2 col-xs-offset-1">
<input name="Test" type="radio" data-bind="checkedValue: false, checked: ServiceFee.IsPercentage" />
<!-- <input type="radio" name="percentageOrFlatRate" value="flatRate" /> -->
</label>
... other code ...
<input class="form-control"
data-bind="textInput: ServiceFee.FlatRate, valueUpdate: 'afterkeydown', disable: ServiceFee.IsPercentage"
data-value="flatRate" id="ServiceFee_Percentage" name="ServiceFee.Percentage"
placeholder="0.00" type="number" value="" />
Upvotes: 2