繁体   English   中英

更新Knockout.js中的observableArray之后,如何立即使用DOM容器?

[英]How can I work with the DOM container immediately after updating an observableArray in Knockout.js?

我已经使用Knockout几天了,这是我整理组织视图模型和javascript模型时想到的:

//******************************************************************************
// 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容器之前,似乎正在运行对超滚动的调用。

由于过度滚动调用位于接收数据后运行的ajax回调内部,因此我认为可以。 Knockout是否异步更新DOM? 我不确定从这里出发...有什么建议吗?

我解决了这个问题:

在jQuery ready函数中,我在加载任何数据之前先对DOM元素进行缓存,然后使用容器元素内部没有任何内容的缓存元素。

所以,我改变了这个:

var panel1 = $('#panel1>section');

至:

var panel1 = '#panel1>section';

然后在ShiftsViewModel中,我更改了这一行:

element.overscroll();

至:

$(element).overscroll();

它现在似乎正在工作...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM