KnockoutJS - disable binding - how to disable child elements when parent has value

I've written a jsfiddle to demonstrate what I'm trying to accomplish below. Have a requirement when a value is entered in the parent (excursion) pay amount input, the child (charge) pay amounts should be disabled. And vice versa, when a pay amount is entered into either child (charge) pay amount input, the parent disables. I've think I have it working perfectly for the parent - it disables both child inputs - but when entering a value in the second charge input, it doesn't disable the parent. It's also hardcoded, so I'm needing help with this part:

charges()[0].payAmount() != '' || charges()[0].payAmount() != 0

I've seen this - Knockoutjs Update child when parent's observable changes - to add a subscriber, but I don't think I can still access/disable the children from there. Or maybe I can, with jQuery? I've also seen this - Is there any way to disable a bunch of form elements at once? - using a binding handler to potentially handle the disabling of the $parent maybe when entering a value in the children's inputs. Seems complicated though. I noticed that in the ko enable binding documentation that you can pass an abitrary expression, but you can't seem to pass in any parameters, like the current object. And, on the function signature, if I add excursion as an argument, I've verified it's undefined. Here's a function I had tried to attach to the disable binding on the parent, to no avail, as encounter is undefined:

function hasChildEntries(encounter) {
    ko.utils.arrayFirst(encounter.charges(), function (charge) {
        console.log(charge + ', ' + charge.payAmount() + '...');
        if (charge.payAmount() != '' || charge.payAmount() != 0)
            return true;
    });
    return false;
};

view:

<div>
    <ul class="payments">
    <!-- ko foreach: excursions -->
    <li class="payments">
        <div class="payments">
        <a href='#' class="expand glyphicon glyphicon-minus-sign" style="color:grey"></a>
        <a href='#' class="collapse glyphicon glyphicon-plus-sign" style="color:grey"></a>
        </div>
        <div class="payments" data-bind="text: 'Excursion ID: ' + id + '&nbsp;&nbsp;Pay Amount: '"></div>
        <input class="payments" data-bind="
            textInput: payAmount,
            valueUpdate: 'afterkeydown',
            disable: charges()[0].payAmount() != '' || charges()[0].payAmount() != 0" />
        <ul class="payments bullet">
        <!-- ko foreach: charges -->
        <li class="payments">
            <div class="payments" data-bind="text: name + '&nbsp;&nbsp;Pay Amount: '"></div>
            <input class="payments" data-bind="
                textInput: payAmount,
                valueUpdate: 'afterkeydown',
                disable: $parent.payAmount() != '' || $parent.payAmount() != 0" />
        </li>
        <!-- /ko -->
        </ul>
    </li>
    <!-- /ko -->
    </ul>
</div>

view model/javascript:

var viewModel = function () {
    var self = this;
    self.excursions = ko.observableArray().extend({ rateLimit: 0 });

    function Excursion(id, name, chgAmount, payAmount, charges) {
    this.id = id;
    this.name = name;
    this.chgAmount = chgAmount;
    this.payAmount = ko.observable('');
    this.charges = ko.observableArray(charges).extend({ rateLimit: 0 });
    };

    function Charge(id, name, date, chgAmount, payAmount) {
    this.id = id;
    this.name = name;
    this.date = date;
    this.chgAmount = chgAmount;
    this.payAmount = ko.observable('');
    };

    self.excursions.push(new Excursion('1234', 'Excursion 1', 90.00, 0, undefined));
    ko.utils.arrayFirst(self.excursions(), function (excursion) {
    if (excursion.id = '1234') {
        excursion.charges.push(new Charge(1, 'Trunk Bay', '02/10/2015', 50.00, 0));
        excursion.charges.push(new Charge(2, 'Cinnamon Bay', '02/10/2015', 40.00, 0));
    }
    });
    self.excursions.push(new Excursion('1235', 'Excursion 2', 80.00, 0, undefined));
    ko.utils.arrayFirst(self.excursions(),  function (excursion) {
    if (excursion.id == '1235')
        excursion.charges.push(new Charge(3, 'Coral Bay', '02/11/2015', 80.00, 0));
    });
}

var vm = new viewModel();
ko.applyBindings(vm);

$(".expand").click(function () { 
    $(this).toggle(); 
    $(this).next().toggle(); 
    $(this).parent().parent().children().last().toggle();
}); 
$(".collapse").click(function () { 
    $(this).toggle(); 
    $(this).prev().toggle(); 
    $(this).parent().parent().children().last().toggle();
});

CSS:

.altRow:nth-child(even) td { background-color: #D8D8D8; }
ul.payments { list-style:none; float:left; width:100% }
li.payments { padding-top:10px; float:left; width:100% }
div.payments { float:left }
.expand { width:15px;height:15px; }
.collapse { width:15px;height:15px;display:none }
ul.payments.bullet {list-style-type: disc;}
input.payments { width:80px;height:20px; }

JSFiddle

Upvotes: 0

Views: 860

Answers (1)

dfperry
dfperry

Reputation: 2258

Add a computed to your excursion model:

this.hasCharges = ko.computed(function() {
  for(var i = 0, len = charges().length; i < len; i++ ) {
    if( charges()[i].payAmount() ) {
      return true;
    }
  }
  return false;
});

and then in your markup, replace

disable: charges()[0].payAmount() != '' || charges()[0].payAmount() != 0"

with

disable: hasCharges

This will disable the parent no matter how many children it has

edit: a fiddle for you: http://jsfiddle.net/8j8k7h1c/33/

try to avoid using 'this' in your functions, scoping gets messed up. I used

var self = this;

to keep the charges observable accessible by the computed function.

Upvotes: 1

Related Questions