Knockout foreach数组复选框无法直观更新

时间:2017-03-18 20:49:40

标签: javascript knockout.js

我有一个json数组,其中包含在foreach数据绑定中创建的元素。 然后我将所选对象保留在该数组中,以便我可以为该数组中的每个对象提供独立的“保存更改”按钮。除了对复选框的绑定外,所有这些都有效(例如primarycontactname)。

<div class="container span8" data-bind="foreach: locationssubscribed">
  <div class="well span3" data-bind="click: $parent.selectedLocationSubscribed">
    <input type="text" class="span3" data-bind="value: primarycontactname" placeholder="Contact Name.." />
    <br />      
    <div class="checkbox" data-bind="visible: (vendorbringinggifts() === 0 || vendorbringinggifts() === vendorid())">
      <input id="chkGiftsAreBeingBrought" type="checkbox" data-bind="checked: giftsarebeingbrought" />
    </div>
    <button data-bind="click: $root.saveVendorToLocation, enable: needsave, text: needsave() ? 'Save Location Changes' : 'No Changes to Save', css: { 'btn-primary': needsave }" class="btn">Save Location Changes</button>
  </div>
</div

根据每个数组对象中可观察到的礼物加载正确加载复选框,但是当单击复选框时,可见检查不会切换。使用调试器我可以看到原始数组和selectedLocationSubscribed中的可观察礼物在第一次点击时切换,但是在后续点击时不再切换,并且在初始绑定后,视觉复选框永远不会改变。

{
    "locationssubscribed": [
    {
      "vendortolocationid": 10,
      "primarycontactname": "Fake Name1",
      "vendorbringinggifts": 0,
      "giftsarebeingbrought": false,
      "needsave": false
    },
    {
      "vendortolocationid": 11,
      "primarycontactname": "Fake Name2",
      "vendorbringinggifts": 0,
      "giftsarebeingbrought": false,
      "needsave": false
    },
    {
      "vendortolocationid": 12,
      "primarycontactname": "Fake Name3",
      "vendorbringinggifts": 0,
      "giftsarebeingbrought": false,
      "needsave": false
    },
    {
      "vendortolocationid": 13,
      "primarycontactname": "Fake Name4",
      "vendorbringinggifts": 0,
      "giftsarebeingbrought": false,
      "needsave": false
    }
  ],
  "selectedLocationSubscribed": {
    "vendortolocationid": 12,
    "primarycontactname": "Fake Name1",
    "vendorbringinggifts": 0,
    "giftsarebeingbrought": true,
    "needsave": true
  }
}



function VendorToLocation(vtl) {
  this.vendortolocationid = ko.observable(vtl.VendorToLocationID);
  this.primarycontactname = ko.observable(vtl.PrimaryContactName);
  this.vendorbringinggifts = ko.observable(vtl.VendorBringingGifts);
  this.giftsarebeingbrought = ko.observable(vtl.GiftsAreBeingBrought);
  this.needsave = ko.observable(false);
}

function VendorViewModel() {
  var self = this;
      self.locationssubscribed = ko.observableArray();
  self.selectedLocationSubscribed = ko.observable();
  self.selectedLocationSubscribed.subscribe(function (ftl) {
    if (ftl !== null) {
      ftl.needsave(true);
    }
  });

  self.getLocationsAvailable = function (vendorID) {
    self.locationsavailable.removeAll();
    $.ajax($("#GetLocationsAvailableUrl").val(), {
      data: '{ "vendorID":' + vendorID + '}',
      async: false,
      success: function (allData) {
        self.locationsavailable($.map(allData, function (item) { return new LocationsAvailable(item) }));
      }
    });
  }

  self.getLocationSubscription = function (vendorID) {
    self.locationssubscribed.removeAll();
    $.ajax($("#GetLocationSubscriptionUrl").val(), {
      data: '{ "vendorID":' + vendorID + '}',
      success: function (allData) {
        self.locationssubscribed($.map(allData, function (item) { return new VendorToLocation(item) }));
      }
    });
  }


  self.saveVendorToLocation = function () {
    var url = $("#updateVendorToLocationUrl").val();
    var vendorid = self.selectedVendor().vendorid();
    var selectedLoc = self.selectedLocationSubscribed();
    $.ajax(url, {
      data: '{ "vtl" : ' + ko.toJSON(selectedLoc) + '}',
      success: function (result) {
        if (result === false) {
          toastr.error("ERROR!:  Either you or a competing vendor has chosen this location since you last loaded the webpage.  Please refresh the page.");
        } else {
          toastr.success("Vendor to location details saved");
          selectedLoc.vendortolocationid(result.VendorToLocationID);
          self.updateVendorView();  // added 170307 1030 to get vendor contact details to update automatically
          self.getActionLog(vendorid);
          selectedLoc.needsave(false);
        }
      }
    });

  };
}   

$(document).ready(function () {

  var myViewModel = new VendorViewModel();
  ko.applyBindings(myViewModel);
  myViewModel.updateVendorView();
  myViewModel.getLocationSubscription(curVendorID);
}

目标是让复选框正常工作。我删除的其他基于文本框的绑定来压缩帖子已经正常工作多年了一些我现在难以理解我在文本框中做错了什么。

2 个答案:

答案 0 :(得分:0)

让我再次确认,因此您尝试启用关联按钮,并在用户点击<div class="well span3 ...>"内的元素时选中复选框。

我直接在代码中提出了所有建议。

&#13;
&#13;
function VendorToLocation(vtl) {
  var self = this;
  self.vendortolocationid = ko.observable(vtl.vendortolocationid);
  self.primarycontactname = ko.observable(vtl.primarycontactname);
  self.vendorbringinggifts = ko.observable(vtl.vendorbringinggifts);
  self.giftsarebeingbrought = ko.observable(vtl.giftsarebeingbrought);
  self.needsave = ko.observable(vtl.needsave);

  // I prefer to put all the logic in here instead of being embedded to the HTML
  self.isCheckboxVisible = ko.pureComputed(function(){
    return self.vendorbringinggifts() === 0 || self.vendorbringinggifts() === self.vendortolocationid();
  });
}

function VendorViewModel() {
  var self = this;
  self.locationssubscribed = ko.observableArray(
    [
      new VendorToLocation ({
        "vendortolocationid": 10,
        "primarycontactname": "Fake Name1",
        "vendorbringinggifts": 0,
        "giftsarebeingbrought": false,
        "needsave": false
      }),
      new VendorToLocation ({
        "vendortolocationid": 11,
        "primarycontactname": "Fake Name2",
        "vendorbringinggifts": 0,
        "giftsarebeingbrought": false,
        "needsave": false
      }),
      new VendorToLocation ({
        "vendortolocationid": 12,
        "primarycontactname": "Fake Name3",
        "vendorbringinggifts": 0,
        "giftsarebeingbrought": false,
        "needsave": false
      }),
      new VendorToLocation ({
        "vendortolocationid": 13,
        "primarycontactname": "Fake Name4",
        "vendorbringinggifts": 0,
        "giftsarebeingbrought": false,
        "needsave": false
      })
    ]
  );
  
  // To store the selected location
  self.selectedLocationSubscribed = ko.observable();

  // To modify selected location, enable the button and modify the checkbox whenever user click on an element that uses this as its click event
  self.selectLocationSubscribed = function(data, event) {
    if(data !== null) { 
      self.selectedLocationSubscribed(data);
    
      // If you want to change needsave of other properties to false (disable all other buttons) before that you can do it here
      ko.utils.arrayForEach(self.locationssubscribed(), function(location) {
        if(data.vendortolocationid() !== location.vendortolocationid()){
          location.needsave(false);
          location.giftsarebeingbrought(false);
        }
      });
      // code ends here
    
      // And then you modify the selected needsave the selected object to true to enable the button
      data.needsave(true);
      data.giftsarebeingbrought(true);
    }

    // To perform the default browser click action
    return true;
  }
}   

$(document).ready(function () {
  var myViewModel = new VendorViewModel();
  ko.applyBindings(myViewModel);
});
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />

<div class="container span8" data-bind="foreach: locationssubscribed">
  <div class="well span3" data-bind="click: $parent.selectLocationSubscribed">
    <input type="text" class="span3" data-bind="value: primarycontactname" placeholder="Contact Name.." />
    <br />      
    <div class="checkbox" data-bind="visible: (vendorbringinggifts() === 0 || vendorbringinggifts() === vendorid())">
      <input id="chkGiftsAreBeingBrought" type="checkbox" data-bind="checked: giftsarebeingbrought" />
    </div>
    <button data-bind="click: $root.saveVendorToLocation, enable: needsave, text: needsave() ? 'Save Location Changes' : 'No Changes to Save', css: { 'btn-primary': needsave }" class="btn">Save Location Changes</button>
  </div>
</div>
&#13;
&#13;
&#13;

答案 1 :(得分:0)

click绑定会阻止默认操作。要启用默认操作return true from the event handler。这意味着您无法直接将observable传递给click绑定。

click: $parent.handleClick

JS:

self.handleClick = function (item) {
    // do something with item
    return true;  // don't prevent default action
}