Это дополнительный вопрос к этому один:
Как объяснено в приведенном выше ответе:
When you provide an expression for a binding value rather than just a reference to an observable, KO effectively wraps that expression in a computed when applying the bindings.
Таким образом, я ожидал, что при предоставлении changeCity в качестве выражения привязки (это функция, а не наблюдаемая), изменение значения в поле input вызовет функцию changeCity.
Однако, как вы можете видеть в первом фрагменте, это не так (и при привязке его как changeCity()), но если changeCity объявлен как ko.computed, он срабатывает - см. Второй фрагмент.
Означает ли это, что ограниченная функция и ограниченная вычисляемая не полностью идентичны в отношении отслеживания зависимостей?
Первый фрагмент - ограниченная функция:
var handlerVM = function () {
var self = this;
self.city = ko.observable("London");
self.country = ko.observable("England");
self.changeCity = function () {
if (self.country() == "England") {
self.city("London");
} else {
self.city("NYC");
}
}
}
ko.applyBindings(new handlerVM());<script src = "https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<h3 data-bind = "text: city"> </h1>
<span data-bind = "text: 'change the country, get out of focus, and nothing will happen...'"></span>
<br/>
<input data-bind = "value: country" />Второй фрагмент - вычислено ограниченное:
var handlerVM = function () {
var self = this;
self.city = ko.observable("London");
self.country = ko.observable("England");
self.changeCity = ko.computed(function () {
if (self.country() == "England") {
self.city("London");
} else {
self.city("NYC")
}
});
}
ko.applyBindings(new handlerVM());<script src = "https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<h3 data-bind = "text: city"> </h1>
<span data-bind = "text: 'change the country, get out of focus, and behold:'"> </span>
<br/>
<input data-bind = "value: country" />@Tomalak, спасибо, исправлено
ko.computed будет правильным решением.
@Tomalak, я смущен, так как ответ, который я связал, говорит с точностью до наоборот, т.е. поскольку ограниченная функция обернута в вычисляемую, она делает имеет отслеживание зависимостей.
Но «вычисляемый» - это именно то, что: функция, заключенная в ko.computed(). В отличие от self.changeCity = function ..., который выполняет простую функцию. В последнем нет отслеживания зависимостей, в первом есть.
Разница, по-видимому, заключается в том, как knockout обрабатывает встроенные выражения, определенные непосредственно в разметке. Я считаю, что ответ, который вы указали, конкретно относился к этому делу. Однако это всего лишь обоснованное предположение; У меня нет источника об этом
В любом случае вы не можете привязать вычисленное к обработчику событий. Это бессмысленно. Что вы ожидаете увидеть во втором примере кода? Вычисленное вычисляет значение, и это его цель. Посылать на него события кликов не имеет смысла. (Кроме того, я не очень понимаю, почему у вас кнопки disabled: true, это тоже не имеет никакого смысла)
@Tomalak, ожидаемое поведение в обоих фрагментах заключается в том, что после изменения текста в поле ввода (и смещения фокуса) changeCity запускается (происходит 2-го, а не 1-го). Кнопки отключены, чтобы указать, что нажатие кнопки не требуется для запуска changeCity, но я согласен, что это сбивает с толку, я отредактирую его.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


the expected behavior in both snippets is that once the text in the input box is changed (and the focus is out), changeCity is fired (Happens on the 2nd, not on the 1st).
Ааа, теперь я понимаю. Вы описываете, что делает подписка.
Во-первых, избавьтесь от событий DOM. Поле <input> не существует. Все, что есть, - это ваша модель просмотра. (*)
С таким мышлением ясно, что делать: реагировать на изменения в вашем свойстве country через .subscribe(). Следующее делает то, что вы имеете в виду.
var handlerVM = function () {
var self = this;
self.city = ko.observable("London");
self.country = ko.observable("England");
self.country.subscribe(function (newValue) {
switch (newValue.toLowerCase()) {
case "england":
self.city("London");
break;
case "usa":
self.city("NYC");
break;
default:
self.city("(unknown)");
}
});
}
ko.applyBindings(new handlerVM());<script src = "https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<h3 data-bind = "text: city"></h3>
<input data-bind = "value: country" />(*) Конечно, поле <input> все еще существует. Но это помогает представить представление (ваш HTML) на 100% зависимым от вашей модели представления. Knockout делает за вас все взаимодействие "модель-представление". Он заботится об отображении изменений в данных модели просмотра и заботится об обратном взаимодействии пользователя с вашей моделью просмотра. Все, на что вам следует обратить внимание, это изменения в вашей модели просмотра.
Всякий раз, когда вы чувствуете, что вам нужно прослушать базовое событие DOM, такое как «щелчок», есть вероятность, что вы делаете что-то не так, то есть, скорее всего, вам не хватает наблюдаемого или пользовательская привязка.
Спасибо, Томалак, но, кажется, меня все еще неправильно поняли. Я пытаюсь уладить то, что вы сказали (обычная функция не отслеживает зависимости), завершив связанный выше ответ, цитируя: «поскольку func использует значение наблюдаемого [...], отслеживание изменений KO видит и запоминает этот факт при вычислении вычисленного значения».
Прости, ты не имеешь смысла. Согласно вашему комментарию, вы хотите, чтобы что-то произошло, и я снова цитирую "после того, как текст в поле ввода будет изменен (и фокус исчезнет)". Это именно то, что делает мой код, а ваш - нет. Для этого вам не нужно какое-либо событие DOM «изменить» или «щелкнуть», и я до сих пор не понимаю, почему вы хотите привязать вычисляемый как обработчик событий.
Tomalak, я пытался спросить о Зачем, некоторые Икс происходят в случае а, а не в случае б в отношении предыдущего ответа SO, а не о как, чтобы это Икс произошло. Ожидаемое поведение, о котором я упоминал, - это просто добавление других деталей к вопросу Зачем. Ваш ответ не относится к предыдущему вопросу, поэтому я не понимаю, как был дан ответ на мой вопрос Зачем. Конечно, я считаю себя виноватым в том, что меня неправильно поняли.
Хорошо, в качестве общего подхода к заданию, пожалуйста, задавайте вопросы как самостоятельные единицы. Нет смысла требовать от читателя, чтобы он сначала прочитал и понял предыдущий вопрос. Никто не станет этим заниматься.
При этом в вашем первом примере кода ничего не происходит, потому что ничто никогда не вызывает функцию changeCity. Думаю, это ясно. Во втором примере кода ваша функция changeCity является наблюдаемой (наблюдаемые - это функции!). Knockout вызывает эту функцию один раз, когда вы создаете модель просмотра, чтобы определить ее значение (значение - undefined, потому что вы никогда ничего не возвращаете). Тем не менее, во время этого вызова функции нокаут замечает, что вы читаете значение country, поэтому он создает зависимость от country. И - о чудо - когда страна меняется, ваша наблюдаемая снова работает.
Измените второй codeSample на pureComputed, и он больше не будет работать, потому что нокаут не запускает чистые вычисления, у которых нет подписчика, поэтому запуск исходный, который происходит на обычном computed (для определения его значения), не происходит в чистом вычисляется, поэтому нет отслеживания зависимостей, следовательно, нет волшебного поведения при изменении country.
Я так понимаю, что вы не просто пытаетесь решить практическую проблему, но вас больше всего интересует «теоретическая разница» между передачей computed или простого function в привязку. Попробую объяснить различия / сходства.
const someObs = ko.observable(10);
const someFn = () => someObs() + 1;
const someComp = ko.computed(someFn);
const dec = () => someObs(someObs() - 1);
ko.applyBindings({ someObs, someFn, someComp, dec });<script src = "https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div>Obs. value: <span data-bind = "text: someObs"></span></div>
<div>Computed created in binding: <span data-bind = "text: someFn()"></span></div>
<div>Computed created in vm: <span data-bind = "text: someComp"></span></div>
<button data-bind = "click: dec">-1</button>Пример выше показывает, что и someFn, и someComp делают одно и то же. Ссылаясь на someFn() в значении обработчика привязки, вы, по сути, создали вычисляемый с зависимостью от someObs.
Вы никогда не ссылались на свой метод changeCity ни в каком коде, связанном с нокаутом, а это значит, что у вас никогда не будет возможности создать зависимость. Конечно, можно форсировать, но это как-то странно:
var handlerVM = function () {
var self = this;
self.city = ko.observable("London");
self.country = ko.observable("England");
self.changeCity = function () {
if (self.country() == "England") {
self.city("London");
} else {
self.city("NYC");
}
}
}
ko.applyBindings(new handlerVM());<script src = "https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<h3 data-bind = "text: city"> </h1>
<span data-bind = "html: 'change the country, get out of focus, and <strike>nothing</strike> <strong>something</strong> will happen...'"></span>
<br/>
<input data-bind = "value: (changeCity(), country)" />computedделает работаетВо втором примере вы используете ko.computed. После создания экземпляра ko.computed переданная функция оценивается один раз (немедленно) и создаются зависимости для всех используемых наблюдаемых объектов.
Если вы замените ko.computed на ko.pureComputed, вы увидите, что ваш второй пример также перестанет работать. pureComputed выполняет оценку только после того, как его возвращаемое значение действительно используется, и до тех пор не будет создавать зависимости.
Нокаут оборачивает значение вашей привязки в функциюкак строка. Подробнее об этом можно прочитать в ответ, который я написал ранее.
Мы также знаем, что любой observable, который вызывается внутри метода init обработчика привязки, создает зависимость, которая вызывает метод привязки update, когда происходит изменение.
Итак, в приведенном мною примере происходит следующее:
text анализируетсяfunction() { return someFn(); } передается как аксессуар значения методу text связывания init.someObs запрашивается его значение, и создается зависимостьЗатем, нажав на кнопку и сменив someObs:
someObs изменяется, запускает метод text связывания updateupdate вызывает valueAccessor, повторно оценивает someObs и правильно обновляет его текст.В заключение несколько практических советов:
Используйте ko.pureComputed, когда вы создаете новое значение из одного или нескольких значений observable. (ваш пример)
self.city = ko.pureComputed(
() => self.country() === "england"
? "london"
: "nyc"
);
subscribe, если вы хотите создать побочные эффекты на основе наблюдаемого изменения значения. Например: console.info нового значения или сброс таймера.ko.computed, если вы хотите создать побочные эффекты на основе изменения любого из нескольких наблюдаемых.Фантастическое, это то объяснение, которое я искал.
ko.applyBindings(handlerVM);не правильный. Вы не можете просто передать функцию конструктора, вам нужно фактически Создайте модели просмотра.ko.applyBindings(new handlerVM());