二维敲除排序不更新UI
Two-dimensional knockout sortable not updating UI
我正在创建一个二维 sortable 容器,第一维(table 中的行)和第二维(一行中的单元格)。
单元格应可在一行内拖动到现有行,拖动到动态创建的新行。应动态删除空行。这些单元格配置为连续占据所有 space。
如何编辑自定义 Knockout 排序器table 绑定(例如 update
事件)?
之前:
之后:
更新问题:
- 拖动单元格时 (
.sortable-cell
)
到新行 (.sortable-table
/.sortable-row
) viewModel 得到更新,但 UI
- 将单元格 (
.sortable-cell
) 拖动到新行 (.sortable-table
/.sortable-row
) 时不显示占位符 (.highlight-horizontal
)
//connect items with observableArrays
ko.bindingHandlers.sortableList = {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
$(element).data("sortList", valueAccessor().data); //attach meta-data
$(element).sortable({
placeholder: valueAccessor().placeholder,
start: function(event, ui) {},
change: function(event, ui) {},
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 = bindingContext.$root;
//figure out its new position
var position = ko.utils.arrayIndexOf(ui.item.parent().children(), ui.item[0]);
if (ui.item.parent()[0].classList.contains("sortable-row")) {
//Row already exists
console.log("true");
} else {
//Row doesn't exist, create new row (PROBLEM WITH UPDATE HERE)
newParent().splice(position, 0, {
"children": ko.observableArray([])
});
newParent = newParent()[position].children;
}
//Update item position
originalParent.remove(item);
newParent.splice(position, 0, item);
//Remove empty lists
var children = viewModel.children();
for (var i = 0; i < children.length; i++) {
if (children[i].children().length == 0) {
viewModel.children.remove(children[i]);
console.log(children);
}
}
//Update UI
ui.item.remove();
//Debug data model
console.log("final viewModel");
var children = viewModel.children();
for (var i = 0; i < children.length; i++) {
console.log(children[i].children());
for (var j = 0; j < children[i].children().length; j++) {
console.log(children[i].children()[j].children(),children[i].children()[j].content());
}
}
}
},
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);
}
};
var self = this;
var viewModel = function() {
var self = this;
self.children = ko.observableArray(
[{
"children": ko.observableArray([{
"content": ko.observable("Item 1"),
"children": ko.observableArray([])
}, {
"content": ko.observable("Item 2"),
"children": ko.observableArray([])
}, {
"content": ko.observable("Item 3"),
"children": ko.observableArray([])
}])
}, {
"children": ko.observableArray([{
"content": ko.observable("Item 4"),
"children": ko.observableArray([])
}])
}, {
"children": ko.observableArray([{
"content": ko.observable("Item 5"),
"children": ko.observableArray([])
}, {
"content": ko.observable("Item 6"),
"children": ko.observableArray([])
}])
}]
);
};
ko.applyBindings(new viewModel());
.sortable-table {
border: 1px red solid;
padding: 10px 0px;
list-style-type: none;
width: 100% !important;
display: table !important;
}
.sortable-table .sortable-row {
height: 100% !important;
display: table-row !important;
padding: 5px 0px;
}
.sortable-table .sortable-cell {
border: 1px solid green;
display: table-cell !important;
cursor: move;
}
.sortable-table .sortable-cell p {
display: inline;
margin: 0 !important;
}
.sortable-table .highlight-vertical {
width: 5px !important;
display: table-cell !important;
background-color: blue !important;
}
.sortable-table .highlight-horizontal {
height: 5px !important;
width: 100% !important;
display: block !important;
background-color: blue !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://rniemeyer.github.com/KnockMeOut/Scripts/jquery.tmpl.js"></script>
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/2.2.1/knockout-min.js"></script>
<div class="sortable-container" data-bind="template: { name: 'rowTmpl', foreach: $data.children, templateOptions: { parentList: $data.children } }, sortableList: { data: $data.children, placeholder: 'highlight-horizontal' }">
</div>
<script id="rowTmpl" type="text/html">
<div class="sortable-table">
<div class="sortable-row sortable-container" data-bind="template: { name: 'cellTmpl', foreach: $data.children, templateOptions: { parentList: $data.children } }, sortableList: { data: $data.children, placeholder: 'highlight-vertical' }">
</div>
</div>
</script>
<script id="cellTmpl" type="text/html">
<div class="sortable-cell" data-bind="sortableItem: { item: $data, parentList: $item.parentList }">
<p data-bind="text: $data.content"></p>
</div>
</script>
问题出在线上 newParent.splice(position, 0, {"children": ko.observableArray([])});
。 newParent
被称为 newParent()
,这是导致问题的原因。
//connect items with observableArrays
ko.bindingHandlers.sortableList = {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
$(element).data("sortList", valueAccessor().data); //attach meta-data
$(element).sortable({
placeholder: valueAccessor().placeholder,
start: function(event, ui) {},
change: function(event, ui) {},
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 = bindingContext.$root;
//figure out its new position
var position = ko.utils.arrayIndexOf(ui.item.parent().children(), ui.item[0]);
if (ui.item.parent()[0].classList.contains("sortable-row")) {
//Row already exists
console.log("true");
} else {
//Row doesn't exist, create new row (PROBLEM WITH UPDATE HERE)
newParent.splice(position, 0, {
"children": ko.observableArray([])
});
newParent = newParent()[position].children;
}
//Update item position
originalParent.remove(item);
newParent.splice(position, 0, item);
//Remove empty lists
var children = viewModel.children();
for (var i = 0; i < children.length; i++) {
if (children[i].children().length == 0) {
viewModel.children.remove(children[i]);
console.log(children);
}
}
//Update UI
ui.item.remove();
//Debug data model
console.log("final viewModel");
var children = viewModel.children();
for (var i = 0; i < children.length; i++) {
console.log(children[i].children());
for (var j = 0; j < children[i].children().length; j++) {
console.log(children[i].children()[j].children(),children[i].children()[j].content());
}
}
}
},
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);
}
};
var self = this;
var viewModel = function() {
var self = this;
self.children = ko.observableArray(
[{
"children": ko.observableArray([{
"content": ko.observable("Item 1"),
"children": ko.observableArray([])
}, {
"content": ko.observable("Item 2"),
"children": ko.observableArray([])
}, {
"content": ko.observable("Item 3"),
"children": ko.observableArray([])
}])
}, {
"children": ko.observableArray([{
"content": ko.observable("Item 4"),
"children": ko.observableArray([])
}])
}, {
"children": ko.observableArray([{
"content": ko.observable("Item 5"),
"children": ko.observableArray([])
}, {
"content": ko.observable("Item 6"),
"children": ko.observableArray([])
}])
}]
);
};
ko.applyBindings(new viewModel());
.sortable-table {
border: 1px red solid;
padding: 10px 0px;
list-style-type: none;
width: 100% !important;
display: table !important;
}
.sortable-table .sortable-row {
height: 100% !important;
display: table-row !important;
padding: 5px 0px;
}
.sortable-table .sortable-cell {
border: 1px solid green;
display: table-cell !important;
cursor: move;
}
.sortable-table .sortable-cell p {
display: inline;
margin: 0 !important;
}
.sortable-table .highlight-vertical {
width: 5px !important;
display: table-cell !important;
background-color: blue !important;
}
.sortable-table .highlight-horizontal {
height: 5px !important;
width: 100% !important;
display: block !important;
background-color: blue !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://rniemeyer.github.com/KnockMeOut/Scripts/jquery.tmpl.js"></script>
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/2.2.1/knockout-min.js"></script>
<div class="sortable-container" data-bind="template: { name: 'rowTmpl', foreach: $data.children, templateOptions: { parentList: $data.children } }, sortableList: { data: $data.children, placeholder: 'highlight-horizontal' }">
</div>
<script id="rowTmpl" type="text/html">
<div class="sortable-table">
<div class="sortable-row sortable-container" data-bind="template: { name: 'cellTmpl', foreach: $data.children, templateOptions: { parentList: $data.children } }, sortableList: { data: $data.children, placeholder: 'highlight-vertical' }">
</div>
</div>
</script>
<script id="cellTmpl" type="text/html">
<div class="sortable-cell" data-bind="sortableItem: { item: $data, parentList: $item.parentList }">
<p data-bind="text: $data.content"></p>
</div>
</script>
我正在创建一个二维 sortable 容器,第一维(table 中的行)和第二维(一行中的单元格)。
单元格应可在一行内拖动到现有行,拖动到动态创建的新行。应动态删除空行。这些单元格配置为连续占据所有 space。
如何编辑自定义 Knockout 排序器table 绑定(例如 update
事件)?
之前:
之后:
更新问题:
- 拖动单元格时 (
.sortable-cell
) 到新行 (.sortable-table
/.sortable-row
) viewModel 得到更新,但 UI - 将单元格 (
.sortable-cell
) 拖动到新行 (.sortable-table
/.sortable-row
) 时不显示占位符 (.highlight-horizontal
)
//connect items with observableArrays
ko.bindingHandlers.sortableList = {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
$(element).data("sortList", valueAccessor().data); //attach meta-data
$(element).sortable({
placeholder: valueAccessor().placeholder,
start: function(event, ui) {},
change: function(event, ui) {},
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 = bindingContext.$root;
//figure out its new position
var position = ko.utils.arrayIndexOf(ui.item.parent().children(), ui.item[0]);
if (ui.item.parent()[0].classList.contains("sortable-row")) {
//Row already exists
console.log("true");
} else {
//Row doesn't exist, create new row (PROBLEM WITH UPDATE HERE)
newParent().splice(position, 0, {
"children": ko.observableArray([])
});
newParent = newParent()[position].children;
}
//Update item position
originalParent.remove(item);
newParent.splice(position, 0, item);
//Remove empty lists
var children = viewModel.children();
for (var i = 0; i < children.length; i++) {
if (children[i].children().length == 0) {
viewModel.children.remove(children[i]);
console.log(children);
}
}
//Update UI
ui.item.remove();
//Debug data model
console.log("final viewModel");
var children = viewModel.children();
for (var i = 0; i < children.length; i++) {
console.log(children[i].children());
for (var j = 0; j < children[i].children().length; j++) {
console.log(children[i].children()[j].children(),children[i].children()[j].content());
}
}
}
},
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);
}
};
var self = this;
var viewModel = function() {
var self = this;
self.children = ko.observableArray(
[{
"children": ko.observableArray([{
"content": ko.observable("Item 1"),
"children": ko.observableArray([])
}, {
"content": ko.observable("Item 2"),
"children": ko.observableArray([])
}, {
"content": ko.observable("Item 3"),
"children": ko.observableArray([])
}])
}, {
"children": ko.observableArray([{
"content": ko.observable("Item 4"),
"children": ko.observableArray([])
}])
}, {
"children": ko.observableArray([{
"content": ko.observable("Item 5"),
"children": ko.observableArray([])
}, {
"content": ko.observable("Item 6"),
"children": ko.observableArray([])
}])
}]
);
};
ko.applyBindings(new viewModel());
.sortable-table {
border: 1px red solid;
padding: 10px 0px;
list-style-type: none;
width: 100% !important;
display: table !important;
}
.sortable-table .sortable-row {
height: 100% !important;
display: table-row !important;
padding: 5px 0px;
}
.sortable-table .sortable-cell {
border: 1px solid green;
display: table-cell !important;
cursor: move;
}
.sortable-table .sortable-cell p {
display: inline;
margin: 0 !important;
}
.sortable-table .highlight-vertical {
width: 5px !important;
display: table-cell !important;
background-color: blue !important;
}
.sortable-table .highlight-horizontal {
height: 5px !important;
width: 100% !important;
display: block !important;
background-color: blue !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://rniemeyer.github.com/KnockMeOut/Scripts/jquery.tmpl.js"></script>
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/2.2.1/knockout-min.js"></script>
<div class="sortable-container" data-bind="template: { name: 'rowTmpl', foreach: $data.children, templateOptions: { parentList: $data.children } }, sortableList: { data: $data.children, placeholder: 'highlight-horizontal' }">
</div>
<script id="rowTmpl" type="text/html">
<div class="sortable-table">
<div class="sortable-row sortable-container" data-bind="template: { name: 'cellTmpl', foreach: $data.children, templateOptions: { parentList: $data.children } }, sortableList: { data: $data.children, placeholder: 'highlight-vertical' }">
</div>
</div>
</script>
<script id="cellTmpl" type="text/html">
<div class="sortable-cell" data-bind="sortableItem: { item: $data, parentList: $item.parentList }">
<p data-bind="text: $data.content"></p>
</div>
</script>
问题出在线上 newParent.splice(position, 0, {"children": ko.observableArray([])});
。 newParent
被称为 newParent()
,这是导致问题的原因。
//connect items with observableArrays
ko.bindingHandlers.sortableList = {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
$(element).data("sortList", valueAccessor().data); //attach meta-data
$(element).sortable({
placeholder: valueAccessor().placeholder,
start: function(event, ui) {},
change: function(event, ui) {},
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 = bindingContext.$root;
//figure out its new position
var position = ko.utils.arrayIndexOf(ui.item.parent().children(), ui.item[0]);
if (ui.item.parent()[0].classList.contains("sortable-row")) {
//Row already exists
console.log("true");
} else {
//Row doesn't exist, create new row (PROBLEM WITH UPDATE HERE)
newParent.splice(position, 0, {
"children": ko.observableArray([])
});
newParent = newParent()[position].children;
}
//Update item position
originalParent.remove(item);
newParent.splice(position, 0, item);
//Remove empty lists
var children = viewModel.children();
for (var i = 0; i < children.length; i++) {
if (children[i].children().length == 0) {
viewModel.children.remove(children[i]);
console.log(children);
}
}
//Update UI
ui.item.remove();
//Debug data model
console.log("final viewModel");
var children = viewModel.children();
for (var i = 0; i < children.length; i++) {
console.log(children[i].children());
for (var j = 0; j < children[i].children().length; j++) {
console.log(children[i].children()[j].children(),children[i].children()[j].content());
}
}
}
},
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);
}
};
var self = this;
var viewModel = function() {
var self = this;
self.children = ko.observableArray(
[{
"children": ko.observableArray([{
"content": ko.observable("Item 1"),
"children": ko.observableArray([])
}, {
"content": ko.observable("Item 2"),
"children": ko.observableArray([])
}, {
"content": ko.observable("Item 3"),
"children": ko.observableArray([])
}])
}, {
"children": ko.observableArray([{
"content": ko.observable("Item 4"),
"children": ko.observableArray([])
}])
}, {
"children": ko.observableArray([{
"content": ko.observable("Item 5"),
"children": ko.observableArray([])
}, {
"content": ko.observable("Item 6"),
"children": ko.observableArray([])
}])
}]
);
};
ko.applyBindings(new viewModel());
.sortable-table {
border: 1px red solid;
padding: 10px 0px;
list-style-type: none;
width: 100% !important;
display: table !important;
}
.sortable-table .sortable-row {
height: 100% !important;
display: table-row !important;
padding: 5px 0px;
}
.sortable-table .sortable-cell {
border: 1px solid green;
display: table-cell !important;
cursor: move;
}
.sortable-table .sortable-cell p {
display: inline;
margin: 0 !important;
}
.sortable-table .highlight-vertical {
width: 5px !important;
display: table-cell !important;
background-color: blue !important;
}
.sortable-table .highlight-horizontal {
height: 5px !important;
width: 100% !important;
display: block !important;
background-color: blue !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://rniemeyer.github.com/KnockMeOut/Scripts/jquery.tmpl.js"></script>
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/2.2.1/knockout-min.js"></script>
<div class="sortable-container" data-bind="template: { name: 'rowTmpl', foreach: $data.children, templateOptions: { parentList: $data.children } }, sortableList: { data: $data.children, placeholder: 'highlight-horizontal' }">
</div>
<script id="rowTmpl" type="text/html">
<div class="sortable-table">
<div class="sortable-row sortable-container" data-bind="template: { name: 'cellTmpl', foreach: $data.children, templateOptions: { parentList: $data.children } }, sortableList: { data: $data.children, placeholder: 'highlight-vertical' }">
</div>
</div>
</script>
<script id="cellTmpl" type="text/html">
<div class="sortable-cell" data-bind="sortableItem: { item: $data, parentList: $item.parentList }">
<p data-bind="text: $data.content"></p>
</div>
</script>