Knockout JS - Обещание рекурсии - PullRequest
0 голосов
/ 18 марта 2019

У меня есть ситуация, в которой я должен обновить наблюдаемую Knockout, привязанную к элементу HTML, и его значение обновляется асинхронной операцией (выборка с сервера).

Я написал следующий пример кода:

const viewModel = function() {
  const self = this;

  self.fetchResults = function() {
    const id = ko.observable(0);
    fetch("https://jsonplaceholder.typicode.com/photos")
      .then(function(response) {
        return response.json();
      })
      .then(function(data) {
        id(data.length);
        console.log(id());
      })
    return id;
  };
};

ko.applyBindings(new viewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div data-bind="text: fetchResults()"></div>

Создает бесконечный рекурсивный цикл.

Вызывает ли Knockout функцию несколько раз, поскольку значение обновляется внутри асинхронной функции?Что может быть основной причиной этой проблемы?

TIA!

Редактировать:

У меня есть несколько элементов HTML на странице, запрашивающих различную информацию длябыть привязанным к ним, например,

<div data-bind="text: fetchResults('some-url')"></div>
<div data-bind="text: fetchResults('some-different-url')"></div>
<div data-bind="text: fetchResults('another-url-altogether')"></div>

Это означало бы, что мне нужно создать наблюдаемую функцию внутри fetchResults, которую я сделал (пожалуйста, исправьте меня, если мое понимание неправильно:))

1 Ответ

1 голос
/ 18 марта 2019

Проблема в том, что, поскольку вы используете выражение, функция запускается при рендеринге, возвращая наблюдаемое.Когда значение наблюдаемой изменяется, она снова выполняет визуализацию той части представления, которая снова вызывает функцию, которая повторяется вечно.

В комментарии вы сказали:

На самом делеМне нужно несколько раз вызвать этот метод для разных URI извлечения на одной странице и связать с отдельными элементами HTML.

... и добавил этот пример к вопросу:

<div data-bind="text: fetchResults('some-url')"></div>
<div data-bind="text: fetchResults('some-different-url')"></div>
<div data-bind="text: fetchResults('another-url-altogether')"></div>

Это немного меняет дело.Я бы, вероятно, использовал пользовательскую привязку, а не функцию:

ko.bindingHandlers.textFetch = {
  update(element, valueAccessor) {
    const url = ko.unwrap(valueAccessor());
    element.textContent = "0";
    fetch(url)
      .then((response) => {
        if (!response.ok) {
          throw new Error("HTTP error " + response.status);
        }
        return response.json();
      })
      .then((data) => {
        element.textContent = data.length;
      });
  }
};
const viewModel = function() {
};

ko.applyBindings(new viewModel());
<div data-bind="textFetch: 'https://jsonplaceholder.typicode.com/photos'"></div>
<div data-bind="textFetch: 'https://jsonplaceholder.typicode.com/posts'"></div>
<div data-bind="textFetch: 'https://jsonplaceholder.typicode.com/comments'"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

Но если вы хотите, чтобы он был более похож на ваш текущий код, сохраняйте кэш наблюдаемых для URL:

const viewModel = function() {
  const self = this;
  // In a modern environment, `observables` could be a Map rather than an object
  const observables = Object.create(null);

  self.fetchResults = function(url) {
    // Get the observable if we have it
    let id = observables[url];
    if (!id) {
      // We don't, create and remember one, do the fetch
      id = observables[url] = ko.observable(0);
      fetch(url)
        .then(function(response) {
          return response.json();
        })
        .then(function(data) {
          id(data.length);
        })
    }
    return id;
  };
};

ko.applyBindings(new viewModel());
<div data-bind="text: fetchResults('https://jsonplaceholder.typicode.com/photos')"></div>
<div data-bind="text: fetchResults('https://jsonplaceholder.typicode.com/posts')"></div>
<div data-bind="text: fetchResults('https://jsonplaceholder.typicode.com/comments')"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

Примечание: Вы не проверяете успешность обратного вызова fetch.Вы не единственный, я бы даже сказал, что API fetch плохо спроектирован, так как я вижу, что этот пистолет стреляет почти каждый день здесь, на SO.Я написал это в своем маленьком анемичном блоге, но в основном вам нужно добавить:

if (!response.ok) {
  throw new Error("HTTP error " + response.status);
}

... в ваш обработчик выполнения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...