简体   繁体   English

带间隔的淘汰Ajax仅显示更改的数据

[英]Knockout Ajax with Interval show only changed data

I have this code: 我有以下代码:

function MyViewModel()
{
  var myviewmodel=this;
  myviewmodel.ajaxData=ko.observableArray([]);


  myviewmodel.init=function()
  {
    updateInterval(function()
    {
      myviewmodel.getData();
    },6000);
  }

  myviewmodel.getData=function()
  {
    myviewmodel.ajaxData([])
    $.get('getData.php')
    .done(function(data)
    {
      if(data.status==='ok' && data.data)
      {
        $.map(data.data,function(f)
        {
          var last_item=new AjaxItem(f);
          myviewmodel.ajaxData.push(last_item);
        })
      }
    })
  }

  function AjaxItem(data)
  {
    var item=this;
    item.id=parseInt(data.id);
    item.name=ko.observable(data.name);
    item.surname=ko.observable(data.surname);
    item.viewed=ko.observable(true);

    setTimeout(function()
    {
       item.viewed(false);
    },600);
  }
}
var vm= new MyViewModel();
ko.applyBindings(vm)
vm.init();

And I view it in this html 我在这个HTML中查看

<!doctype html>
<html>
<head>
........
<script src="somejs.js"></script>

<style>
.green{
 background-color:green;
}
</style>
</head>
<body>
  <table>
    <tbody data-bind="foreach:ajaxData">
      <tr data-bind="css{'green:viewed'}">
       <td data-bind="text:name"></td><td data-bind="text:surname"></td>
      </tr>
    </tbody>
  </table>
</boby>
</html>

The problem is that when I do the call I have to refill the array ajaxData and there is some sort of 'blink' when I do that what I want is to find any items changed and show only them. 问题是,当我执行调用时,我必须重新填充数组ajaxData,并且当我执行该操作时,我会想要查找所有更改的项目并仅显示它们。

There are 3 cases of difference: 有3种差异:

  1. Id exists in but name and surname are different then I want to change the name and surname. ID存在,但名称和姓氏不同,那么我想更改名称和姓氏。
  2. Item exists in Javascript does not exist in the data returned from call therefore I want be removed from ajaxData 在Javascript中存在的项目在调用返回的数据中不存在,因此我想从ajaxData中删除
  3. Item does not exist in the returned data therefore I want to be added at the end of ajaxData observable array. 该项目在返回的数据中不存在,因此我想在ajaxData可观察数组的末尾添加。

Do you fellows have an Idea how to do that. 你们有一个想法如何做到这一点。

NOTE: The problem is HOW to do the comparisons. 注意:问题是如何进行比较。 How I will know that is one returned value exists in the already returned data or not. 我怎么知道这是一个返回值存在于已经返回的数据与否。

Your three requirements are exactly what the Knockout Mapping Plugin will do for you. 您的三个要求正是Knockout映射插件将为您完成的。

But before that, there are several things to fix in your general approach. 但是在此之前,您的一般方法需要解决几件事。

First, let's get the Ajax stuff out of your viewmodel. 首先,让我们从视图模型中删除Ajax内容。 How about an object that collects all your API calls in one place and exposes them as functions? 一个对象如何将所有API调用收集到一个地方并将其公开为函数呢?

This exposes .getData() (which in turn uses .get() internally). 这将公开.getData() (内部又使用.get() )。

var API = {
  get: function (url, params) {
    return $.get(url, params, null, "json").then(function (result) {
      if (result.status === 'ok') return result;
      console.error("Error status for " + url + ": " + result.status, result);
    }).fail(function (jqXhr, status, error) {
      console.error("Could not get " + url + ", error is: " + error);
    });
  },
  getData: function(params) {
    return this.get('getData.php', params);
  }
};

Next, don't call your viewmodels "Viewmodel". 接下来,不要将您的视图模型称为“视图模型”。 That's their purpose, it should not be their name. 那是他们的目的,不应该是他们的名字。 You seem to have two types of things here, items and a list of items. 您似乎在这里有两种类型的内容,项目和项目列表。 I don't know what your items actually are, so I stuck with the name Item , you can choose an even better one. 我不知道您的商品实际上是什么,所以我坚持使用Item ,您可以选择一个更好的商品。 So let's make two constructors: 因此,让我们构造两个构造函数:

function Item(data) {
  var self = this;
  self.id = parseInt(data.id);
  self.name = ko.observable(data.name);
  self.surname = ko.observable(data.surname);
  self.viewed = ko.observable(true);
  setTimeout(function () {
    self.viewed(false);
  }, 600);
}

function ItemList() {
  var self = this;
  self.data = ko.observableArray();

  self.init = function (data) {
    ko.mapping.fromJS(data, ItemList.mapping, self);
  };
  self.load = function () {
    API.getData().done(self.init);
  };
}

Note how the API gets called which makes the ItemList viewmodel much easier to read. 请注意, API如何调用的,这使得ItemList视图模型更易于阅读。 Also note how separating load and init enables you to initialize your viewmodel with data you did not get via Ajax (localStorage maybe?). 还要注意,将loadinit分开将使您能够使用未通过Ajax获得的数据(可能是localStorage?)来初始化视图模型。

You can see how we already call ko.mapping.fromJS here. 您可以在此处查看我们如何调用ko.mapping.fromJS In order to map an incoming object into a functioning viewmodel, the viewmodel properties should be called like the data properties. 为了将传入的对象映射到正常运行的视图模型中,应像数据属性一样调用视图模型属性。

Assuming your Ajax response looks like this: 假设您的Ajax响应如下所示:

{
  status: "ok",
  data: [
    {id: 1, name: "Doctor", surname: "Evil"},
    {id: 2, name: "Austin", surname: "Powers"}
  ]
}

There are two properties, status and data . 有两个属性,即statusdata

  • status we want to ignore, it's not important for the function of the viewmodel. 我们要忽略的status ,对于视图模型的功能并不重要。
  • data contains a list of objects that should become Item instances in our observable array of the same name. data包含一个对象列表,这些对象应成为可观察数组中具有相同名称的Item实例。

To do this, the mapping plugin needs instructions. 为此,映射插件需要说明。 We can slap them onto the ItemList object for convenience. 为了方便起见,我们可以将它们拍打到ItemList对象上。

ItemList.mapping = {
  ignore: ["status"],
  data: {
    key: function (data) {
      return ko.unwrap(data.id);
    },
    create: function (options) {
      return new Item(options.data);
    }
  }
};

This tells the mapping plugin not to bother with status , but to do two things with the objects in data : 这告诉映射插件不要理会status ,而要对data的对象做两件事:

  • The key function will be used to determine the ID of an object, so the mapping plugin knows when an incoming object should be used to update an existing viewmodel. key函数将用于确定对象的ID,因此映射插件知道何时应使用传入的对象更新现有的视图模型。 We use the id property for that. 我们为此使用id属性。
  • The create function will be used to turn the incoming plain objects into Item instances. create函数将用于将传入的普通对象转换为Item实例。

Now every time ko.mapping.fromJS is called on fresh data that came from the server, knockout will cherry-pick existing objects and only update properties that have changed, instead of redrawing the entire list. 现在,每次对来自服务器的新数据调用ko.mapping.fromJS ,敲除操作都会挑选现有对象,并仅更新已更改的属性,而不是重新绘制整个列表。 Missing objects will be removed from the screen, new objects will be added. 缺少的对象将从屏幕上删除,将添加新的对象。


The view stays the same: 视图保持不变:

<table>
    <tbody data-bind="foreach: data">
        <tr data-bind="css: {green: viewed}">
            <td data-bind="text: name"></td>
            <td data-bind="text: surname"></td>
        </tr>
    </tbody>
</table>

Putting it all together (expand and run the example): 将所有内容放在一起(展开并运行示例):

 var API = { get: function (url, params) { return $.get(url, params, null, "json").then(function (result) { if (result.status === 'ok') return result; console.error("Error status for " + url + ": " + result.status); }).fail(function (jqXhr, status, error) { console.error("Could not get " + url + ", error is: " + error); }); }, getData: function(params) { return this.get('getData.php', params); } }; // ------------------------------------------------------------------- function Item(data) { var self = this; self.id = parseInt(data.id); self.name = ko.observable(data.name); self.surname = ko.observable(data.surname); self.viewed = ko.observable(true); setTimeout(function () { self.viewed(false); }, 600); } function ItemList() { var self = this; self.data = ko.observableArray(); self.init = function (data) { ko.mapping.fromJS(data, ItemList.mapping, self); }; self.load = function () { API.getData().done(self.init); }; } ItemList.mapping = { ignore: ["status"], data: { key: function (data) { return ko.unwrap(data.id); }, create: function (options) { return new Item(options.data); } } }; // ------------------------------------------------------------------- // Ajax mockup $.mockjax({ url: "getData.php", responseText: { status: "ok", data: [ {id: 1, name: "Doctor", surname: "Evil"}, {id: 2, name: "Austin", surname: "Powers"} ] } }); var vm = new ItemList(); ko.applyBindings(vm); vm.load(); setInterval(vm.load, 6000); 
 .green { background-color: green; } 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-mockjax/1.6.2/jquery.mockjax.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script> <table> <tbody data-bind="foreach: data"> <tr data-bind="css: {green: viewed}"> <td data-bind="text: name"></td> <td data-bind="text: surname"></td> </tr> </tbody> </table> <hr> <pre data-bind="text: ko.toJSON($root, null, 2)"></pre> 

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

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