Knockout.js - 使用新值每5秒更新一次页面

时间:2022-12-03 12:53:58

I am just looking into knockout.js with MVC-Web-API and I am trying to create a Hello World page that will update the time on the page every 5 seconds. It is making the call every 5 seconds, I can see this in my controller (breakpoint), but still nothing displays on screen.

我只是用MVC-Web-API查看knockout.js,我正在尝试创建一个Hello World页面,它将每5秒更新页面上的时间。它每隔5秒拨打一次电话,我可以在我的控制器(断点)中看到这个,但屏幕上仍然没有显示。

UPDATE: I have still been working on this, and I have now established that I am getting the data back from the server, the call is being made to the controller every 5 seconds, and it is returning the JSON I need (alerts are showing this) however there is still nothing displaying on the span element on the page.

更新:我一直在努力解决这个问题,现在我已经确定我从服务器获取数据,每5秒钟调用一次控制器,并返回我需要的JSON(警报显示但是,页面上的span元素上仍然没有显示任何内容。

I realistically need to use the mapping function as I am developing a larger website, that has a model with over 50 properties and don't particularly want to go through and map them individually to in the viewmodel.

我实际上需要使用映射功能,因为我正在开发一个更大的网站,它有一个包含50多个属性的模型,并且不特别想要在viewmodel中单独映射它们。

I have included my code below.

我在下面提供了我的代码。

<span data-bind="text: TimeString"></span>

<script type="text/javascript">
    var viewModel;
var getUpdates = setInterval(function () {
    $.getJSON(
        "/Values/Get", {},
        function (model) {
            alert(model.TimeString);
            ko.mapping.fromJS(model, viewModel);
        });
}, 5000);

$(document).ready(
    function () {
        $.getJSON(
            "/Values/Get", {},
            function (model) {
                var viewModel = ko.mapping.fromJS(model);
                alert(model.TimeString);
                ko.applyBindings(viewModel);
            });
    });

function bindViewModel(model) {
    ko.applyBindings(model);
}

public class HelloWorldModel
{
    public DateTime TimeDT { get; set; }
    public String TimeString { get; set; }
}

    public class ValuesController : Controller
{
    public HelloWorldModel Model = new HelloWorldModel();

    [System.Web.Mvc.AcceptVerbs(HttpVerbs.Get)]
    public JsonResult Get()
    {
        Model.TimeDT = DateTime.Now;
        Model.TimeString = Model.TimeDT.ToString("HH:mm:ss");

        return Json(Model, JsonRequestBehavior.AllowGet);
    }

    // POST api/values
    public void Post([FromBody]string value)
    {
    }

    // PUT api/values/5
    public void Put(int id, [FromBody]string value)
    {
    }

    // DELETE api/values/5
    public void Delete(int id)
    {
    }
}
}

3 个解决方案

#1


6  

If you follow the documentation, it shouldn't be too hard. In your first call to the server, do:

如果你按照文档,它应该不会太难。在第一次调用服务器时,请执行以下操作:

var viewModel = ko.mapping.fromJS(model);
ko.applyBindings(viewModel);

You are applying bindings with a JS object (getJSON returns a JS object, not a JSON string, if I'm correctly reading the documentation).

您正在使用JS对象应用绑定(如果我正确阅读文档,则getJSON返回JS对象,而不是JSON字符串)。

After that, in your repeated function, do:

之后,在您的重复功能中,执行:

ko.mapping.fromJS(model, viewModel);

From the documentation:

从文档:

  • All properties of an object are converted into an observable. If an update would change the value, it will update the observable.
  • 对象的所有属性都将转换为可观察对象。如果更新将更改该值,它将更新observable。

  • Arrays are converted into observable arrays. If an update would change the number of items, it will perform the appropriate add/remove actions. It will also try to keep the order the same as the original JavaScript array.
  • 数组被转换为可观察的数组。如果更新将更改项目数,它将执行适当的添加/删除操作。它还会尝试保持顺序与原始JavaScript数组相同。

#2


4  

You don't need replace the complete view-model and can instead update the properties returned from your Ajax request, like this:

您不需要替换完整的视图模型,而是可以更新从Ajax请求返回的属性,如下所示:

$(function() {
    var vm = {
        TimeDT: ko.observable(),
        TimeString: ko.observable()
    };

    function updateValues() {    
        $.getJSON("/Values/Get").done(function(data) {
            vm.TimeDT(data.TimeDT);
            vm.TimeString(data.TimeString);
        });
    }

    ko.applyBindings(vm);

    updateValues();
    setInterval(updateValues, 5000);
});

You can see here a small example I made in JsFiddle.

你可以在这里看到我在JsFiddle中做的一个小例子。

#3


3  

Disclaimer: I work with Ben.

免责声明:我与Ben合作。

There are a few issues with the code, the first is you are missing some javascript references, secondly the viewModel object is always null each time the timer loops through.

代码有一些问题,第一个是你缺少一些javascript引用,其次,每次定时器循环时,viewModel对象总是为null。

You need to download the knockout mapping Javascript file from here, name it knockout.mapping-latest.js and save it in the Scripts directory. Then make sure the references to jquery and knockout.js are added.

您需要从此处下载淘汰映射Javascript文件,将其命名为knockout.mapping-latest.js并将其保存在Scripts目录中。然后确保添加对jquery和knockout.js的引用。

An updated Razor view is below:

更新后的Razor视图如下:

<div id="body">

    <span data-bind='text: TimeString'></span>

    <script src="~/Scripts/jquery-1.8.2.js" type="text/javascript"></script>
    <script src="~/Scripts/knockout-2.2.0.debug.js" type="text/javascript"></script>
    <script src="~/Scripts/knockout.mapping-latest.js" type="text/javascript"></script>
    <script type="text/javascript">
        var viewModel;
        var getUpdates = setInterval(function () {
            $.getJSON(
                "/Values/Get", {},
                function (model) {
                    //alert(model.TimeString);
                    ko.mapping.fromJS(model, viewModel);
                });
        }, 5000);

        var viewModelSet = false;

        $(document).ready(
            function () {
                $.getJSON(
                    "/Values/Get", {},
                    function (model) {
                        viewModel = ko.mapping.fromJS(model);
                        ko.applyBindings(viewModel);
                    });
            });

        function bindViewModel(model) {
            ko.applyBindings(model);
        }
    </script>    

</div>

Just to add, public fields in a class are generally a no-go, in your ViewModel your HelloWorldModel instance doesn't get accessed from anywhere else in the code, just from within that class so it can be private. If it was accessed from elsewhere, the best practise would be to keep it private, and expose it through a property. More info here.

只是要添加,类中的公共字段通常是禁止的,在您的ViewModel中,您的HelloWorldModel实例不会从代码中的任何其他位置访问,只能从该类中访问,因此它可以是私有的。如果从其他地方访问它,最佳做法是将其保密,并通过属性公开。更多信息在这里。

#1


6  

If you follow the documentation, it shouldn't be too hard. In your first call to the server, do:

如果你按照文档,它应该不会太难。在第一次调用服务器时,请执行以下操作:

var viewModel = ko.mapping.fromJS(model);
ko.applyBindings(viewModel);

You are applying bindings with a JS object (getJSON returns a JS object, not a JSON string, if I'm correctly reading the documentation).

您正在使用JS对象应用绑定(如果我正确阅读文档,则getJSON返回JS对象,而不是JSON字符串)。

After that, in your repeated function, do:

之后,在您的重复功能中,执行:

ko.mapping.fromJS(model, viewModel);

From the documentation:

从文档:

  • All properties of an object are converted into an observable. If an update would change the value, it will update the observable.
  • 对象的所有属性都将转换为可观察对象。如果更新将更改该值,它将更新observable。

  • Arrays are converted into observable arrays. If an update would change the number of items, it will perform the appropriate add/remove actions. It will also try to keep the order the same as the original JavaScript array.
  • 数组被转换为可观察的数组。如果更新将更改项目数,它将执行适当的添加/删除操作。它还会尝试保持顺序与原始JavaScript数组相同。

#2


4  

You don't need replace the complete view-model and can instead update the properties returned from your Ajax request, like this:

您不需要替换完整的视图模型,而是可以更新从Ajax请求返回的属性,如下所示:

$(function() {
    var vm = {
        TimeDT: ko.observable(),
        TimeString: ko.observable()
    };

    function updateValues() {    
        $.getJSON("/Values/Get").done(function(data) {
            vm.TimeDT(data.TimeDT);
            vm.TimeString(data.TimeString);
        });
    }

    ko.applyBindings(vm);

    updateValues();
    setInterval(updateValues, 5000);
});

You can see here a small example I made in JsFiddle.

你可以在这里看到我在JsFiddle中做的一个小例子。

#3


3  

Disclaimer: I work with Ben.

免责声明:我与Ben合作。

There are a few issues with the code, the first is you are missing some javascript references, secondly the viewModel object is always null each time the timer loops through.

代码有一些问题,第一个是你缺少一些javascript引用,其次,每次定时器循环时,viewModel对象总是为null。

You need to download the knockout mapping Javascript file from here, name it knockout.mapping-latest.js and save it in the Scripts directory. Then make sure the references to jquery and knockout.js are added.

您需要从此处下载淘汰映射Javascript文件,将其命名为knockout.mapping-latest.js并将其保存在Scripts目录中。然后确保添加对jquery和knockout.js的引用。

An updated Razor view is below:

更新后的Razor视图如下:

<div id="body">

    <span data-bind='text: TimeString'></span>

    <script src="~/Scripts/jquery-1.8.2.js" type="text/javascript"></script>
    <script src="~/Scripts/knockout-2.2.0.debug.js" type="text/javascript"></script>
    <script src="~/Scripts/knockout.mapping-latest.js" type="text/javascript"></script>
    <script type="text/javascript">
        var viewModel;
        var getUpdates = setInterval(function () {
            $.getJSON(
                "/Values/Get", {},
                function (model) {
                    //alert(model.TimeString);
                    ko.mapping.fromJS(model, viewModel);
                });
        }, 5000);

        var viewModelSet = false;

        $(document).ready(
            function () {
                $.getJSON(
                    "/Values/Get", {},
                    function (model) {
                        viewModel = ko.mapping.fromJS(model);
                        ko.applyBindings(viewModel);
                    });
            });

        function bindViewModel(model) {
            ko.applyBindings(model);
        }
    </script>    

</div>

Just to add, public fields in a class are generally a no-go, in your ViewModel your HelloWorldModel instance doesn't get accessed from anywhere else in the code, just from within that class so it can be private. If it was accessed from elsewhere, the best practise would be to keep it private, and expose it through a property. More info here.

只是要添加,类中的公共字段通常是禁止的,在您的ViewModel中,您的HelloWorldModel实例不会从代码中的任何其他位置访问,只能从该类中访问,因此它可以是私有的。如果从其他地方访问它,最佳做法是将其保密,并通过属性公开。更多信息在这里。