[英]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种差异:
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?). 还要注意,将
load
和init
分开将使您能够使用未通过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
. 有两个属性,即
status
和data
。
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
的对象做两件事:
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
属性。 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.