将 Knockout observable 传递给 jQueryUI sortable
Passing Knockout observable to jQueryUI sortable
在这里,我试图将对 Knockout observable 的引用传递给一个可排序对象,该对象是 jQueryUI 的一部分,默认情况下无法访问该变量。
在代码示例中,我试图在行 self.dragMode(true);
上通过 self
引用 viewModel
。该行在代码示例中突出显示。
尝试的调用失败,每当我调用引用的对象时,我都会得到 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());
.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;
}
<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>
ko.bindingHandlers.yourBindingName
的参数是:element, valueAccessor, allBindings, viewModel, bindingContext
(从这里 - knockoutjs manual)。
在您的代码中,您可能将其作为 context
参数。
因此您可以将 self.dragMode(true);
更改为 context.dragMode(true)
,它应该可以工作。
在这种情况下,有必要使用 bindingContext
,其中包含对全局 viewModel 的引用。无法使用 viewModel
参数,因为上下文根据本地使用模板的位置而改变。
最后,这些是获取 viewModel
并将其传递给 jQuery 可排序所需的所有更改:
$(element).data("viewModel", bindingContext.$root);
var viewModel = ui.item.parent().data("viewModel");
更新绑定处理程序:
//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'
});
}
};
在这里,我试图将对 Knockout observable 的引用传递给一个可排序对象,该对象是 jQueryUI 的一部分,默认情况下无法访问该变量。
在代码示例中,我试图在行 self.dragMode(true);
上通过 self
引用 viewModel
。该行在代码示例中突出显示。
尝试的调用失败,每当我调用引用的对象时,我都会得到 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());
.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;
}
<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>
ko.bindingHandlers.yourBindingName
的参数是:element, valueAccessor, allBindings, viewModel, bindingContext
(从这里 - knockoutjs manual)。
在您的代码中,您可能将其作为 context
参数。
因此您可以将 self.dragMode(true);
更改为 context.dragMode(true)
,它应该可以工作。
在这种情况下,有必要使用 bindingContext
,其中包含对全局 viewModel 的引用。无法使用 viewModel
参数,因为上下文根据本地使用模板的位置而改变。
最后,这些是获取 viewModel
并将其传递给 jQuery 可排序所需的所有更改:
$(element).data("viewModel", bindingContext.$root);
var viewModel = ui.item.parent().data("viewModel");
更新绑定处理程序:
//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'
});
}
};