【问题标题】:Knockout foreach array checkboxes are not visually updating淘汰赛 foreach 数组复选框未在视觉上更新
【发布时间】:2017-03-21 04:47:06
【问题描述】:

我有一个 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);
}

目标是让复选框正常工作。我为压缩帖子而删除的其他基于文本框的绑定多年来一直正常工作,有些我现在对我在文本框做错了什么感到困惑。

【问题讨论】:

  • 好的,所以我发现如果我删除“click: $parent.selectedLocationSubscribed”,症状就会消失,但可以观察到的是我如何对对象进行一些 isDirty 样式跟踪,以便我知道该面板/对象是否需要允许用户保存他们的更改。任何想法如何将检查的绑定重新应用到元素?

标签: javascript knockout.js


【解决方案1】:

让我再次确认,如果用户点击&lt;div class="well span3 ...&gt;" 中的元素,您将尝试启用关联按钮并选中复选框。

我把所有的建议都直接放在代码里了。

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);
});
<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>

【讨论】:

  • 我已经实现了这一点,但问题仍然存在,单击时复选框没有明显变化,但使用 toJSON 调用进行调试显示底层可观察对象在第一次单击时发生变化。所有后续点击都不会改变任何东西。当 selectedLocationSubscribed 更改时,就好像复选框正在失去它的“已检查”数据绑定。是否有任何其他想法可以确保元素保留其复选框功能?
  • 感谢您的帮助!现在正在工作。我非常感谢您为此花费的时间。
【解决方案2】:

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
}

【讨论】:

    猜你喜欢
    • 2015-08-27
    • 2014-02-21
    • 1970-01-01
    • 2015-05-28
    • 2014-02-22
    • 2013-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多