返回可观察的函数就像递归调用一样

时间:2015-02-26 19:54:49

标签: knockout.js

我有一个<select>,其中填充了数据库中的列表。我使用jquery ajax调用来获取数据。起初我设置了async = false,因此呼叫是同步的,一旦呼叫完成,我就会更新我的VM。我试图通过使用promises来避免同步调用,但我遇到了一个问题。 (为了简化这个例子,我删除了承诺。)

我以为我可以创建一个函数来调用ajax并返回一个observable。然后,一旦ajax调用完成,observable将被更新,并且<select>中将看到新的值。我遇到的问题是,如果我使用普通函数,它的行为类似于递归调用,并且observable一遍又一遍地更新。如果我使用计算的可观察量,则不会发生这种情况。我之所以要使用函数而不是计算函数,是因为我可以传递其他参数。(我的理解是你可以将参数传递给observable)

此时,我认为我最好的选择是使用计算机并尝试找出一种不同的方式来获取参数,但我想理解为什么使用普通函数不起作用

我创建了一个plunk来显示:(在select元素上将getAvailableCountries()更改为getAvailableCountries2()以查看区别) http://plnkr.co/v1Q0bBhWOAHQSv8N1vwu

HTML:

<!DOCTYPE html>
<html>

<head>
  <link rel="stylesheet" href="style.css" />

  <script data-require="jquery@*" data-semver="2.1.3" src="https://code.jquery.com/jquery-2.1.3.min.js"></script>

  <script src="//cdnjs.cloudflare.com/ajax/libs/jquery-mockjax/1.5.3/jquery.mockjax.js"></script>
  <script data-require="knockout@*" data-semver="3.2.0" src="//cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

  <script src="mockJax.js"></script>
  <script src="script.js"></script>
</head>

<body>
  <section>
    <label class="control-label">Your country:</label>
      <select class="form-control" data-bind="options: getAvailableCountries(),
                       optionsText: 'countryName',
                       value: selectedCountry,
                       optionsCaption: 'Choose...'"></select>
    <div data-bind="visible: selectedCountry">
      <hr>
      <label class="control-label">You have chosen a country with population:</label>
        <span data-bind="text: selectedCountry() ? selectedCountry().countryPopulation : 'unknown'"></span>
      <hr>
    </div>
  </section>
  <br>
  <footer>
    <label>ko.toJSON():</label><br>
    <span data-bind="text: ko.toJSON($data, null, 2)"></span>
  </footer>
</body>
</html>

JS:

var Country = function(name, population) {
  this.countryName = name;
  this.countryPopulation = population;
};

var viewModel = function() {
  var availableCountries = ko.observableArray([new Country("First Country", 5000)]);
  var selectedCountry = ko.observable();

  function callAjax() {
    $.mockjax({
      url: '/Countries',
      responseTime: 2000,
      responseText: [{
        population: 1000,
        name: "Country A"
      }, {
        population: 2000,
        name: "Country B"
      }, {
        population: 3000,
        name: "Country C"
      }]
    });
    $.ajax({
      dataType: "json",
      url: "/Countries",
      success: function(data, status, jqXhr) {
        for (i = 0; i < data.length; i++) {
          var tmpData = data[i];
          var tmpCountry = new Country(tmpData.name, tmpData.population);
          availableCountries.push(tmpCountry);
        }
      },
    });
  }

  var getAvailableCountries = ko.computed(function() {
    callAjax();
    return availableCountries;
  });

  var getAvailableCountries2 = function() {
    callAjax();
    return availableCountries;
  };

  var testAjax = function() {
    $.ajax({
      dataType: "json",
      url: "/Countries",
      success: function(data, status, jqXhr) {
        var x = 1;
      }
    });
  };

  return {
    availableCountries: availableCountries,
    selectedCountry: selectedCountry,
    getAvailableCountries: getAvailableCountries,
    getAvailableCountries2: getAvailableCountries2,
    testAjax: testAjax,
  };
};

$(document).ready(function() {
  ko.applyBindings(viewModel);
});

在审核了已接受的答案之后,请查看以下内容:http://plnkr.co/edit/LvFWVG3pDrVbvPvLm7Cu

1 个答案:

答案 0 :(得分:1)

  

此时,我认为我最好的选择是使用计算机并尝试找出一种不同的方式来获取参数,但我想理解为什么使用普通函数不起作用

Knockout中的绑定处理程序使用计算的observable在绑定中的任何可观察对象更新时更新UI。当发生这样的更新时,绑定处理程序重新读取绑定值,这会导致再次调用该函数。无论绑定本身是如何构造的,此方法都可确保绑定始终获得正确的最新值。

getAvailableCountries使用计算的observable可以在跟踪何时从服务器获取更新数据与何时使用该数据更新视图之间进行分离。当您需要加载新数据以响应其他可观察对象时,这尤其有用,并且在Knockout wiki上对此模式进行了很好的解释。