将Knockout observable传递给jQueryUI可排序

时间:2016-01-19 17:39:28

标签: knockout.js jquery-ui-sortable knockout-sortable

我试图将Knockout observable的引用传递给一个可排序对象,该对象是jQueryUI的一部分,并且默认情况下无法访问该变量。

在代码示例中,我尝试在行viewModel上引用selfself.dragMode(true);。该代码示例中突出显示该行。

尝试的调用失败,每当我调用引用的对象时,我得到undefined。这样做的正确方法是什么?



var viewModel = function() {
  var self = this;
  self.gridItems = ko.observableArray(
    [{
      "rowItems": [{
        "name": "Item 1"
      }, {
        "name": "Item 2"
      }, {
        "name": "Item 3"
      }]
    }, {
      "rowItems": [{
        "name": "Item 4"
      }]
    }, {
      "rowItems": [{
        "name": "Item 5"
      }, {
        "name": "Item 6"
      }]
    }]
  );
  self.selectedRowItem = ko.observable();
  self.dragMode = ko.observable(false);

  console.log(self.gridItems());

  self.gridItems().splice(0, 0, {
    "rowItems": [{
      "placeholder": true,
      "name": ""
    }]
  });

  for (var i = 1; i < self.gridItems().length; i++) {
    console.log(self.gridItems()[i]);
    self.gridItems()[i].rowItems.splice(0, 0, {
      "placeholder": true,
      "name": ""
    });
    for (var j = 1; j < self.gridItems()[i].rowItems.length; j++) {
      self.gridItems()[i].rowItems.splice(j + 1, 0, {
        "placeholder": true,
        "name": ""
      });
      j++;
    }
    self.gridItems().splice(i + 1, 0, {
      "rowItems": [{
        "placeholder": true,
        "name": ""
      }]
    });
    i++;
  }

  console.log(self.gridItems());
};

//connect items with observableArrays
ko.bindingHandlers.sortableList = {
  self: this,
  init: function(element, valueAccessor, allBindingsAccessor, context) {
    $(element).data("sortList", valueAccessor()); //attach meta-data
    $(element).sortable({
      start: function(event, ui) {
      	self.dragMode(true); // HERE NEED TO ACCESS VIEWMODEL
      },
      update: function(event, ui) {
        var item = ui.item.data("sortItem");
        if (item) {
          //identify parents
          var originalParent = ui.item.data("parentList");
          var newParent = ui.item.parent().data("sortList");
          //figure out its new position
          var position = ko.utils.arrayIndexOf(ui.item.parent().children(), ui.item[0]);
          if (position >= 0) {
            if (newParent[position].placeholder) {
            	originalParent.remove(item);
            	newParent.splice(position, 0, item);
            } else {
            	return;
            }
          }

          ui.item.remove();
        }
      },
      cancel: ':input,button,.contenteditable,.sortable-placeholder-horizontal,.sortable-placeholder-vertical',
      connectWith: '.sortable-container'
    });
  }
};

//attach meta-data
ko.bindingHandlers.sortableItem = {
  init: function(element, valueAccessor) {
    var options = valueAccessor();
    $(element).data("sortItem", options.item);
    $(element).data("parentList", options.parentList);
  }
};

ko.applyBindings(new viewModel());
&#13;
.sortable-grid .sortable {
  list-style-type: none;
  margin: 0;
  padding: 0;
  width: 100% !important;
  display: table !important;
  table-layout: auto;
}

.sortable-grid .sortable .sortable-item {
  margin: 0 3px 3px 3px;
  padding: 0.4em;
  font-size: 1.4em;
  cursor: move;
}

.sortable-grid .sortable div.fixed {
  cursor: default;
  color: #959595;
  opacity: 0.5;
}

.sortable-grid {
  
}

.sortable-grid .sortable .sortable-row {
  height: 100% !important;
  padding: 0 !important;
  margin: 0 !important;
  display: table-row !important;
}

.sortable-grid .sortable .sortable-item {
  border: 1px solid green;
  //width:initial;
  display: table-cell;
  margin: 0 !important;
}

.sortable-grid .sortable .sortable-item > p {
  //width:100%;
  display: inline;
  margin: 0 !important;
  z-index: 9999;
  cursor: text;
}

.sortable-grid .sortable .sortable-placeholder-horizontal {
  background-color: red;
}

.sortable-grid .sortable .sortable-placeholder {
  background-color: red;
  display: table-cell;
  margin: 0 !important;
  width:10px !important;
}

.sortable-grid .sortable .sortable-placeholder:hover {
  background-color: blue;
}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://code.jquery.com/ui/1.12.0-beta.1/themes/smoothness/jquery-ui.css" rel="stylesheet" />
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" rel="stylesheet" />
<link href="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<div class="" data-bind="template: { name: 'gridTmpl', foreach: gridItems, templateOptions: { parentList: gridItems} }, sortableList: gridItems">
</div>

<script id="gridTmpl" type="text/html">
  <div class="sortable-grid">
    <div class="sortable sortable-container">
      <div class="sortable-row sortable sortable-container" data-bind="template: { name: 'rowTmpl', foreach: rowItems, templateOptions: { parentList: rowItems} }, sortableList: rowItems">
      </div>
    </div>
  </div>
</script>

<script id="rowTmpl" type="text/html">
  <!-- ko if: !$data.placeholder -->
  <div class="sortable-item" data-bind="sortableItem: { item: $data, parentList: $data.parentList }">
    <p class="contenteditable" contenteditable="true" data-bind="text: name"></p>
  </div>
  <!-- /ko -->
  <!-- ko if: $data.placeholder && $root.dragMode -->
  <div class="sortable-placeholder" data-bind="sortableItem: { item: $data, parentList: $data.parentList }">
    </p>
  </div>
  <!-- /ko -->
</script>
&#13;
&#13;
&#13;

2 个答案:

答案 0 :(得分:1)

ko.bindingHandlers.yourBindingName的论据是:element, valueAccessor, allBindings, viewModel, bindingContext(来自此处 - knockoutjs manual)。

在您的代码中,您可能将其作为context参数。

因此,您可以将self.dragMode(true);更改为context.dragMode(true),它应该有效。

答案 1 :(得分:0)

在这种情况下,必须使用bindingContext,其中包含对全局viewModel的引用。无法使用viewModel参数,因为上下文根据本地使用模板的位置而更改。

最后,这些是获取viewModel并将其传递给jQuery sortable所需的所有更改:

  1. $(element).data("viewModel", bindingContext.$root);
  2. var viewModel = ui.item.parent().data("viewModel");
  3. 更新了绑定处理程序:

    //connect items with observableArrays
    ko.bindingHandlers.sortableList = {
      init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        $(element).data("sortList", valueAccessor()); //attach meta-data
        $(element).data("viewModel", bindingContext.$root);// ? bindingContext.$root : bindingContext); //attach meta-data
        $(element).sortable({
          start: function(event, ui) {
            //identify viewModel
            var viewModel = ui.item.parent().data("viewModel");
            viewModel.dragMode(true);
          },
          change: function(event, ui) {
            //identify viewModel
            var viewModel = ui.item.parent().data("viewModel");
            viewModel.dragMode(true);
          },
          update: function(event, ui) {
            var item = ui.item.data("sortItem");
            if (item) {
    
              //identify parents
              var originalParent = ui.item.data("parentList");
              var newParent = ui.item.parent().data("sortList");
              //identify viewModel
              var viewModel = ui.item.parent().data("viewModel");
              //figure out its new position
              var position = ko.utils.arrayIndexOf(ui.item.parent().children(), ui.item[0]);
              if (position >= 0) {
                console.log(newParent[position]);
                if (newParent[position].placeholder) {
                  console.log(originalParent);
                    originalParent.remove(item);
                    newParent.splice(position, 0, item);
                    viewModel.dragMode(false);
                } else {
                    return;
                }
              }
    
              ui.item.remove();
            }
          },
          cancel: ':input,button,.contenteditable,.sortable-placeholder-horizontal,.sortable-placeholder-vertical',
          connectWith: '.sortable-container'
        });
      }
    };