0

我有一个绑定到 observableArray 的模板

<ul data-bind="template: { name: 'defaultTemplate', foreach: itemsArray}"></ul>
...

而且我每次都需要通过单击链接来刷新数据

<a data-bind="click: LoadData"><span>Refresh</span></a>

我的 viewModel 的第一个版本:

function viewModel (){  
    this.itmesArray = ko.observableArray([]);
    self = this;
    this.LoadData(){
        if('undefined' != typeof MusicArray )
            self.itmesArray.removeAll();
        LoadDataFromServerAsync(self.itmesArray);
    }   
    //ini
    LoadData();
    ...
}

但问题是数据是从服务器异步加载的,所以当我以很小的时间间隔快速单击链接时,数据可能会在我的可观察数组中重复多次。所以我认为我需要在每次刷新时将数据加载到一个新数组中。

然后是viewModel的第二个版本:

function viewModel (){  
    this.itmesArray = ko.observableArray([]);
    self = this;
    this.LoadData(){
            if('undefined' != typeof MusicArray )
                self.itmesArray.removeAll();
            var tempArray = new Array();
            LoadDataFromServerAsync(tempArray);
            self.itmesArray(tempArray);
    }   
    //ini
    LoadData();
    ...
}

而新的问题是UI无法感知数组的变化,似乎self.itmesArray(tempArray)会构造一个新的observableArray对象,但是模板绑定仍然在跟踪旧的observableArray对象,我不确定这个. 所以我想知道如何通知模板/ui 绑定我的数组已更改,或者是否有任何其他解决方法来解决我的问题?

补充:jsFiddle上的代码 http://jsfiddle.net/zzcJC/10/

4

4 回答 4

1

你可以按照吉恩的建议去做。为了在客户端完成所有操作,您可以创建一个封装逻辑的辅助对象:它触发请求,获取响应,具有停止请求的“中止”方法(如果用户在等待数据时按下“刷新” ),并且仅在所有数据到达时才更新视图模型。

我创建了一个 jsFiddle:http: //jsfiddle.net/saurus/dE6S9/

这使用“setTimeout()”来模拟 ajax 调用,将超时 id 存储在一个数组中(您将存储 ajax 调用返回的 xhr),并调用“cancelTimeout()”来中止调用(您将使用“xhr.abort 或相似的)。

于 2011-12-30T10:02:33.953 回答
0

如果您在异步模式下加载数据,问题可能是该行

self.itmesArray(tempArray);

之前执行的方式

LoadDataFromServerAsync(tempArray);

已经完成从服务器加载新数据,所以它一直绑定一个空数组。

要修复它,您应该在服务器返回新数据后进行绑定。例如,使用 jquery 进行 ajax 调用,您应该在“成功”回调中更新视图模型。这是我的做法:

    $.ajax({
      type: "POST",
      url: "ws.asmx/GetData",
      data: "{}",
      contentType: "application/json; charset=utf-8",
      dataType: "json",
      processData: false,
      success: function(msg) {
           viewModel.itmesArray(msg);
      }
    });

如果这对您没有帮助,我认为我们需要查看“LoadDataFromServerAsync”实现。

于 2011-12-29T11:18:18.053 回答
0

所以,如果我理解正确,你想点击“刷新”,这会触发一些 ajax 调用,并且从这些调用返回的数据完全替换旧数据。

一种方法是不确定每个呼叫是否独立为其他呼叫工作。例如,执行我在评论中快速建议的操作:将每个调用分开,当单个调用返回新数据时,从数组中删除旧数据并添加新数据。这假设您可以区分不同调用返回的元素。

另一种方法是完全避免多次调用,您可以阻止 UI 交互直到完成(例如使用 jquery blockui 插件或类似的东西),或者如果您想让用户在更新界面时继续使用界面,您可以简单地避免在一些仍在进行中时发起新请求。

两种方式都需要知道请求系列何时开始,但这很容易:这发生在“LoadData”方法的开头。知道所有请求何时完成有点复杂,也许有更好的方法,如果没有,您可以创建一个计数器,为您触发的每个 ajax 请求增加 1,然后,对于每个完成的请求,“推送”新数据在 observableArray 上,并递减计数器(在 jquery 中我会使用“完成”回调,因为即使在出现错误的情况下也可以调用它)。在“LoadData”的一开始(甚至在清除数组之前)添加一个测试:

if (counter > 0) return;

如果您使用“blockui”方式,您应该在递减后检查每个“完成”回调中的计数器,如果它为零,则该解锁 UI(“blockui”选项需要一些工作以避免在 viewModel 中执行 UI 操作)。

除非我还缺少什么...

于 2011-12-29T16:23:59.733 回答
0

也许处理重叠请求的一种方法是在客户端中保留一个用户操作计数器,并将该值传递给服务器,假设您可以控制服务器发回的内容。然后,您可以忽略具有过期用户操作值的响应。如果您无权访问服务器数据,您仍然可以通过将操作计数器值粘贴到成功处理程序中来使用此方法。这是此代码的草图。(警告:未经测试)

function viewModel() {
    var self = this;
    var actionCounter = 0;
    var items = ko.observableArray([]);


    // This is triggered by a user request
    this.handleUserAction = function() {
        self.clearData();
        self.loadData(0, ++actionCounter); // assume 0=root
    }

    this.clearData = function() {
        this.items.removeAll();
    }

    // return some handle of the next request, or null
    this.moreRequestsRequired = function(data) {
         ...
         return ...;
    }

    this.processData = function(data, reqNo) {
        // this is where you would append it to the array
        ...

        // this is where the recursive call could be made
        var handle = self.moreRequestsRequired(data);
        if (handle)
            loadData(handle, reqNo);
    }

    this.loadData = function(handle, n) {
        var reqNumber = n;
        $.ajax('server/request', {
            data: {dir: handle},
            success: function(data) {
                if (reqNumber == actionCounter)
                   processData(data);
            }
        });
    }
};
于 2011-12-29T18:42:58.043 回答