我一直在使用淘汰賽現在幾天,這是我想出組織我的視圖模型和JavaScript型號:如何在更新Knockout.js中的observableArray後立即使用DOM容器?
//******************************************************************************
// jQUERY START:
//******************************************************************************
$(document).ready(function()
{
var panel1 = $('#panel1>section');
var panel2 = $('#panel2>section');
$('#loader').ajaxStart(function()
{
$(this).fadeIn();
}).ajaxComplete(function()
{
$(this).fadeOut();
});
ko.applyBindings(new ViewModel(panel1));
});
//******************************************************************************
// VIEW MODEL:
//******************************************************************************
function ViewModel(_panel1)
{
var self = this;
this.vmHeader = new HeaderViewModel();
this.vmPanel1 = new Panel1ViewModel(_panel1);
}
//******************************************************************************
// Panel1ViewModel:
//******************************************************************************
function Panel1ViewModel(_element)
{
var self = this;
self.element = _element;
self.filters = ['Operations', 'Jobs', 'Shifts', 'Hours'];
self.selectedFilter = ko.observable();
self.vmOperations = new OperationsViewModel();
self.vmJobs = new JobsViewModel();
self.vmShifts = new ShiftsViewModel();
self.vmHours = new HoursViewModel();
//PUBLIC METHODS:
self.clickFilter = function(filter)
{
self.selectedFilter(filter);
switch(filter)
{
case 'Operations':
self.vmOperations.load(self.clear, self.element);
break;
case 'Jobs':
self.vmJobs.load(self.clear, self.element);
break;
case 'Shifts':
self.vmShifts.load(self.clear, self.element);
break;
case 'Hours':
self.vmHours.load(self.clear, self.element);
break;
}
}
self.clear = function()
{
//test each view model to see if it currently has items loaded and empty them.
if (self.vmOperations.operations() != null) { self.vmOperations.operations(null); }
if (self.vmJobs.jobs() != null) { self.vmJobs.jobs(null); }
if (self.vmShifts.shifts() != null) { self.vmShifts.shifts(null); }
if (self.vmHours.hours() != null) { self.vmHours.hours(null); }
};
}
//******************************************************************************
// ShiftsViewModel:
//******************************************************************************
function ShiftsViewModel()
{
var self = this;
self.shifts = ko.observableArray(null);
self.selected = ko.observable();
//PUBLIC METHODS:
self.load = function (callback, element)
{
var options = {
url: '/api/shifts',
type: 'GET',
data: {
operationID: 1
}
};
async_load(options, function (data)
{
callback();
self.shifts(
$.map(data.Items, function (item, index)
{
return new Shift(item);
})
);
element.overscroll(); <--- PROBLEM IS HERE!
});
}
self.click = function(shift)
{
self.selected(shift.id);
};
}
//******************************************************************************
// SHIFT MODEL:
//******************************************************************************
function Shift(data)
{
var self = this;
this.operation = data.Operation;
this.shopOrder = data.ShopOrder;
this.date = data.Date;
this.id = data.ID;
this.number = data.Number;
this.start = ko.observable(data.Start);
this.end = ko.observable(data.End);
this.isRunning = ko.observable(data.IsRunning);
//computed properties.
this.shiftDate = ko.computed(function()
{
var date = (this.end() == '') ? new Date() : new Date(this.end());
return date.toLocaleDateString();
}, this);
this.startTime = ko.computed(function()
{
return (new Date(this.start())).toLocaleTimeString();
}, this);
this.endTime = ko.computed(function()
{
var time = '---';
if (this.isRunning() == 'False')
{
time = (new Date(this.end())).toLocaleTimeString();
}
return time;
}, this);
}
//******************************************************************************
// GLOBAL FUNCTIONS:
//******************************************************************************
function async_load(options, callback)
{
$.ajax({
url: options.url,
async: true,
cache: false,
type: options.type,
data: options.data,
dataType: 'json',
contentType: 'application/json',
success: callback,
error: function (request, type, errorThrown)
{
error_handler(options.url, request, type, errorThrown);
}
});
}
function sync_load(options)
{
var error = false;
var data = $.ajax({
url: options.url,
async: false,
cache: false,
type: options.type,
data: options.data,
dataType: 'json',
contentType: 'application/json',
error: function (request, type, errorThrown)
{
error = true;
error_handler(options.url, request, type, errorThrown);
}
}).responseText;
if (!error)
{
data = eval('(' + data + ')');
}
return (error) ? null : data;
}
function error_handler(name, request, type, errorThrown)
{
switch (request.status)
{
case 404:
alert('The ' + name + ' could not be found.');
break;
case 500:
alert('There was an internal server error loading ' + name + '.');
//redirect the user to a page with further instructions.
break;
default:
alert('An error occurred: (' + request.status + ' ' + request.statusText + ').');
}
}
這裏是HTML:
<section id="panel1" data-bind="with: vmPanel1">
<nav data-bind="foreach: filters">
<a href="#" data-bind="text: $data, css: { selected: $data == $root.vmPanel1.selectedFilter() }, click: $root.vmPanel1.clickFilter"></a>
</nav>
<section>
<section id="panel1Data">
<!-- ko foreach: vmOperations.operations -->
<article class="operation" data-bind="css: { selected: $data.operationID == $root.vmPanel1.vmOperations.selected() }, click: $root.vmPanel1.vmOperations.click">
<div class="name" data-bind="text: name"></div>
<div class="number" data-bind="text: number"></div>
<div class="sequence" data-bind="text: sequence"></div>
</article>
<!-- /ko -->
<!-- ko foreach: vmJobs.jobs -->
<article data-bind="pageBreak: operation, label: 'operation', level: 1"></article>
<article class="job" data-bind="css: { selected: $data.jobID == $root.vmPanel1.vmJobs.selected() }, click: $root.vmPanel1.vmJobs.click">
<div class="start date">
<label data-bind="text: startMonth"></label>
<div data-bind="text: startDay"></div>
<span data-bind="text: startTime"></span>
</div>
<div class="end date">
<label data-bind="text: endMonth"></label>
<div data-bind="text: endDay"></div>
<span data-bind="text: endTime"></span>
</div>
<div class="shoporder" data-bind="text: shopOrder"></div>
<div class="toolconfig" data-bind="toolConfig: $data"></div>
<div class="lot" data-bind="text: lot"></div>
</article>
<!-- /ko -->
<!-- ko foreach: vmShifts.shifts -->
<article data-bind="pageBreak: operation, label: 'operation', level: 1"></article>
<article data-bind="pageBreak: shopOrder, label: 'job', level: 2"></article>
<article data-bind="pageBreak: shiftDate(), level: 3"></article>
<article class="shift" data-bind="css: { selected: $data.id == $root.vmPanel1.vmShifts.selected() }, click: $root.vmPanel1.vmShifts.click">
<div class="shift" data-bind="text: number"></div>
<div class="start time" data-bind="text: startTime()"></div>
<div class="end time" data-bind="text: endTime()"></div>
</article>
<!-- /ko -->
根據所選的過濾器,適當的數據列表(操作,作業,班次或小時數)將被加載到面板1中,jQuery Overscroll插件用於創建IPad滾動效果。
問題出在調用Overscroll插件的行上的ShiftsViewModel。在調試時,我注意到插件沒有做任何事情,因爲容器元素沒有寬度/高度。在運行程序時,容器會使用正確的數據進行更新,因此在通過Knockout將數據寫入DOM容器之前,看起來好像調用了overscroll。
由於overscroll調用在收到數據後運行的ajax回調中,所以我認爲這樣可以。 Knockout異步更新DOM?我不知道該從哪裏出發......有什麼建議嗎?