0

私は数日間 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 を更新していますか? ここからどこに行けばいいのかわからない...何か提案はありますか?

4

1 に答える 1

0

私は問題を理解しました:

jQueryのready関数では、データが読み込まれる前にDOM要素をキャッシュしてから、container要素内に何も含まれていないキャッシュされた要素を使用していました。

だから、私はこれを変更しました:

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

に:

var panel1 = '#panel1>section';

そして、ShiftsViewModelで、次の行を変更しました。

element.overscroll();

に:

$(element).overscroll();

そしてそれは今働いているようです...

于 2012-08-10T15:12:10.810 に答える