У меня есть массив значений true / false / undefined, который я отображаю как список флажков.
При изменении элемента массива на значение true или наоборот, список флажков перерисовывается со следующим флажком (индекс + 1), наследующим изменение вместе с измененным флажком.
Код:
{{#each range as |value idx|}}
<label><input type="checkbox" checked={{value}} {{action makeChange idx on="change"}}>{{idx}}: {{value}}</label><br/>
{{/each}}
Когда я использую {{#each range key="@index" as |value idx|}}
он работает правильно.
Twiddle: https://ember-twiddle.com/6d63548f35f99da19cee9f58fb64db59
@andrewtimberlake похоже, что использование {{#each range key="@index" as |value idx|}}
решает проблему.
Но похоже на ошибку, key
предназначен для другой цели, https://www.emberjs.com/api/ember/release/classes/Ember.Templates.helpers/methods/if?anchor= каждый
Думаю, я знаю, что здесь происходит. Это беспорядка, но я постараюсь это описать. Этому способствовало множество крайних случаев (пользовательская ошибка пограничной линии), и я не совсем уверен, что является / не является ошибкой, что и как исправить любую из этих ошибок.
Прежде всего, мне нужно описать, что делает параметр key
в {{#each}}
. TL; DR он пытается определить, когда и имеет ли смысл повторно использовать существующую DOM, а не просто создавать DOM с нуля.
Для нашей цели давайте примем как данность, что «прикосновение к DOM» (например, обновление содержимого текстового узла, атрибута, добавление или удаление содержимого и т. Д.) Является дорогостоящим и его следует избегать по мере возможности.
Давайте сосредоточимся на довольно простом шаблоне:
<ul>
{{#each this.names as |name|}}
<li>{{name.first}} {{to-upper-case name.last}}</li>
{{/each}}
</ul>
Если this.names
- это ...
[
{ first: "Yehuda", last: "Katz" },
{ first: "Tom", last: "Dale" },
{ first: "Godfrey", last: "Chan" }
]
Тогда вы получите ...
<ul>
<li>Yehuda KATZ</li>
<li>Tom DALE</li>
<li>Godfrey CHAN</li>
</ul>
Все идет нормально.
А что, если мы добавим в список { first: "Andrew", last: "Timberlake" }
? Мы ожидаем, что шаблон создаст следующую модель DOM:
<ul>
<li>Yehuda KATZ</li>
<li>Tom DALE</li>
<li>Godfrey CHAN</li>
<li>Andrew TIMBERLAKE</li>
</ul>
Но как_?
Самый наивный способ реализовать помощник {{#each}}
очищать все содержимое списка каждый раз, когда содержимое списка изменяется. Для этого вам потребуется выполнить не менее 23 операций:
<li>
узлов<li>
to-upper-case
4 разаЭто кажется ... очень ненужным и дорогим. Мы _ знаем_, что первые три элемента не изменились, поэтому было бы неплохо, если бы мы могли просто пропустить работу с этими строками.
Лучшей реализацией было бы попытаться повторно использовать существующие строки и не делать ненужных обновлений. Одна из идей - просто сопоставить строки с их положением в шаблонах. По сути, это то, что делает key="@index"
:
{ first: "Yehuda", last: "Katz" }
с первой строкой <li>Yehuda KATZ</li>
:to-upper-case
, и поэтому нам известен вывод этого помощника («KATZ» ) _тоже_ не изменилось, так что делать здесь нечего<li>
to-upper-case
("Тимберлейк" -> "ТИМБЕРЛЕЙК")Таким образом, с помощью этой реализации мы сократили общее количество операций с 23 до 5 (размахивая руками над стоимостью сравнений, но для нашей цели мы предполагаем, что они относительно дешевы по сравнению с остальными). Неплохо.
Но теперь, что произойдет, если вместо _appnding_ { first: "Andrew", last: "Timberlake" }
в список мы _prepended_ вместо этого? Мы ожидаем, что шаблон создаст следующую модель DOM:
<ul>
<li>Andrew TIMBERLAKE</li>
<li>Yehuda KATZ</li>
<li>Tom DALE</li>
<li>Godfrey CHAN</li>
</ul>
Но как_?
{ first: "Andrew", last: "Timberlake" }
с первой строкой <li>Yehuda KATZ</li>
:to-upper-case
{ first: "Yehuda", last: "Katz" }
со второй строкой, <li>Tom DALE</li>
, еще 3 операции{ first: "Tom", last: "Dale" }
со второй строкой, <li>Godfrey CHAN</li>
, еще 3 операции<li>
to-upper-case
("Чан" -> "ЧАН")Это 14 операций. Ой!
Это казалось ненужным, потому что концептуально, независимо от того, добавляем мы или добавляем, мы все еще изменяем (вставляем) только один объект в массив. Оптимально, мы должны иметь возможность обрабатывать этот случай так же хорошо, как и в сценарии добавления.
Здесь на помощь приходит key="@identity"
. Вместо того, чтобы полагаться на _order_ элементов в массиве, мы используем их идентификатор объекта JavaScript ( ===
):
===
) первому объекту { first: "Andrew", last: "Timberlake" }
. Поскольку ничего не найдено, вставьте (добавьте) новую строку:<li>
to-upper-case
("Тимберлейк" -> "ТИМБЕРЛЕЙК")===
) второму объекту { first: "Yehuda", last: "Katz" }
. Найдено <li>Yehuda KATZ</li>
:to-upper-case
, и поэтому нам известен вывод этого помощника («KATZ» ) _тоже_ не изменилось, так что делать здесь нечегоНа этом мы вернулись к оптимальным 5 операциям.
Опять же, это размахивая рукой над сравнениями и бухгалтерскими расходами. В самом деле, они тоже не бесплатны, и в этом очень простом примере они могут не того стоить. Но представьте, что список большой, и каждая строка вызывает сложный компонент (с множеством помощников, вычисляемых свойств, подкомпонентов и т. Д.). Представьте, например, новостную ленту LinkedIn. Если мы не сопоставим правильные строки с правильными данными, аргументы ваших компонентов могут потенциально сильно сбиваться и вызывать гораздо больше обновлений DOM, чем вы могли бы ожидать. Также существуют проблемы с сопоставлением неправильных элементов DOM и потерей состояния DOM, такого как положение курсора и состояние выделения текста.
В целом, в реальном приложении затраты на дополнительное сравнение и бухгалтерский учет в большинстве случаев окупаются. Поскольку key="@identity"
является значением по умолчанию в Ember и хорошо работает почти во всех случаях, вам обычно не придется беспокоиться об установке аргумента key
при использовании {{#each}}
.
Но подождите, есть проблема. Что насчет этого дела?
const YEHUDA = { first: "Yehuda", last: "Katz" };
const TOM = { first: "Tom", last: "Dale" };
const GODFREY = { first: "Godfrey", last: "Chan" };
this.list = [
YEHUDA,
TOM,
GODFREY,
TOM, // duplicate
YEHUDA, // duplicate
YEHUDA, // duplicate
YEHUDA // duplicate
];
Проблема здесь в том, что один и тот же объект _ мог_ появляться несколько раз в одном списке. Это нарушает наш наивный алгоритм @identity
, в частности ту часть, где мы сказали: «Найдите существующую строку, данные которой совпадают ( ===
) ...» - это работает только в том случае, если отношение данных к DOM равно 1 : 1, что в данном случае неверно. На практике это может показаться маловероятным, но мы должны с этим справиться.
Чтобы избежать этого, мы используем своего рода гибридный подход для обработки этих коллизий. Внутренне сопоставление ключей к DOM выглядит примерно так:
"YEHUDA" => <li>Yehuda...</li>
"TOM" => <li>Tom...</li>
"GODFREY" => <li>Godfrey...</li>
"TOM-1" => <li>Tom...</li>
"YEHUDA-1" => <li>Yehuda...</li>
"YEHUDA-2" => <li>Yehuda...</li>
"YEHUDA-3" => <li>Yehuda...</li>
По большей части это встречается довольно редко, а когда все же возникает, большую часть времени это работает достаточно хорошо ™. Если по какой-то причине это не сработает, вы всегда можете использовать путь ключа (или даже более продвинутый механизм ввода ключей в RFC 321 ).
После всего этого разговора мы готовы взглянуть на сценарий в Twiddle.
По сути, мы начали с этого списка: [undefined, undefined, undefined, undefined, undefined]
.
Несвязанное примечание:
Array(5)
_не_ то же самое, что[undefined, undefined, undefined, undefined, undefined]
. Он создает "дырявый массив", которого в целом следует избегать. Однако это не связано с этой ошибкой, потому что при доступе к «дырам» вы действительно получаетеundefined
обратно. Так что только для нашей _ очень узкой_ цели они одинаковы.
Поскольку мы не указали ключ, Ember по умолчанию использует @identity
. Далее, поскольку это столкновения, мы получили примерно следующее:
"undefined" => <input ...> 0: ...,
"undefined-1" => <input ...> 1: ...,
"undefined-2" => <input ...> 2: ...,
"undefined-3" => <input ...> 3: ...,
"undefined-4" => <input ...> 4: ...
Теперь, допустим, мы установили первый флажок:
{{action}}
и повторно отправляется методу makeChange
[true, undefined, undefined, undefined, undefined]
.Как обновляется DOM?
===
) первому объекту true
. Поскольку ничего не найдено, вставьте (добавьте) новую строку <input checked=true ...>0: true...
===
) второму объекту undefined
. Найдено <input ...>0: ...
(ранее ПЕРВАЯ строка):{{idx}}
до 1
===
) третьему объекту undefined
. Поскольку это второй раз, когда мы видим undefined
, внутренний ключ - undefined-1
, поэтому мы нашли <input ...>1: ...
(ранее ВТОРАЯ строка):{{idx}}
до 2
undefined-2
и undefined-3
undefined-4
(поскольку после обновления в массиве на одну undefined
меньше)Это объясняет, как мы получили результат, который вы получили в твиддле. По сути, все строки DOM смещены вниз на одну, и новая была вставлена вверху, а {{idx}}
обновляется для остальных.
Действительно неожиданная часть - 2.2. Несмотря на то, что первый флажок (тот, который был нажат) был сдвинут на одну строку вниз во вторую позицию, вы, вероятно, ожидали, что Ember, его свойство checked
изменилось на true
, и поскольку его связанное значение не определено, вы можете ожидать, что Ember вернет его обратно на false
, сняв таким образом отметку.
Но это не так. Как упоминалось в начале, доступ к DOM стоит дорого. Это включает _reading_ из DOM. Если бы при каждом обновлении нам приходилось считывать последнее значение из DOM для наших сравнений, это в значительной степени нарушило бы цель наших оптимизаций. Поэтому, чтобы этого избежать, мы запомнили последнее значение, которое мы записали в DOM, и сравнили текущее значение с кэшированным значением, не считывая его обратно из DOM. Только когда есть разница, мы записываем новое значение в DOM (и кэшируем его в следующий раз). В этом смысле мы как бы разделяем тот же подход «виртуального DOM», но мы делаем это только на конечных узлах, не виртуализируя «древовидность» всего DOM.
Итак, TL; DR, «привязка» свойства checked
(или свойства value
текстового поля и т. Д.) На самом деле не работает так, как вы ожидаете. Представьте, что вы визуализировали <div>{{this.name}}</div>
и вручную обновили textContent
элемента div
используя jQuery
или с помощью инспектора Chrome. Вы не ожидали, что Ember заметит это и обновит за вас this.name
. По сути, это то же самое: поскольку обновление свойства checked
произошло за пределами Ember (через поведение браузера по умолчанию для флажка), Ember не узнает об этом.
Вот почему существует помощник {{input}}
. Он должен зарегистрировать соответствующие прослушиватели событий в базовом элементе HTML и отразить операции в соответствующем изменении свойства, чтобы заинтересованные стороны (например, уровень визуализации) могли быть уведомлены.
Я не уверен, к чему это нас приведет. Я понимаю, почему это удивительно, но я склонен сказать, что это серия досадных ошибок пользователя. Возможно, нам следует избегать привязки этих свойств к элементам ввода?
Соответствующий код:
https://github.com/emberjs/ember.js/blob/c24bc23e4139c90c8d8d96c4234d9c0c19e5c594/packages/@ember/ -internals / glimmer / lib / utils / iterable.ts # L390-L391
https://github.com/emberjs/ember.js/blob/c24bc23e4139c90c8d8d96c4234d9c0c19e5c594/packages/@ember/ -internals / glimmer / lib / utils / iterable.ts # L436-L445
https://github.com/emberjs/ember.js/blob/c24bc23e4139c90c8d8d96c4234d9c0c19e5c594/packages/@ember/ -internals / glimmer / lib / utils / iterable.ts # L451-L466
@chancancode - спасибо за прекрасное объяснение. Означает ли это, что никогда нельзя использовать <input ... >
а использовать только {{input ...}}
, чтобы предотвратить все подобные ошибки?
@ boris-petrov могут быть некоторые ограниченные случаи, когда это приемлемо ... например, текстовое поле только для чтения для типа "скопировать этот URL в буфер обмена", или вы _ можете_ использовать элемент ввода + {{action}}
для перехвата DOM и отразите обновления свойств вручную (это то, что пытался сделать твиддл, за исключением того, что он также столкнулся с столкновением @identity
), но да, в какой-то момент вы просто повторно реализуете {{input}}
и обрабатывать все крайние случаи, которые он уже обработал для вас. Поэтому я думаю, что _ вероятно_ справедливо сказать, что вам следует просто использовать {{input}}
большую часть, если не все время.
Однако это все равно не «исправило» этот случай, когда есть столкновения с ключами. См. Https://ember-twiddle.com/0f2369021128e2ae0c445155df5bb034?openFiles=templates.application.hbs%2C
Вот почему я сказал: я на 100% не уверен, что с этим делать. С одной стороны, я согласен, что это удивительно и неожиданно, с другой стороны, такой вид коллизий довольно редко встречается в реальных приложениях, и именно поэтому аргумент «ключ» можно настраивать (это тот случай, когда по умолчанию используется ключ «@identity» функция не является Good Enough ™, поэтому она существует).
@chancancode - это напоминает мне о другом выпуске, который я открыл некоторое время назад . Как вы думаете, там есть что-то подобное? Полученный там ответ (о необходимости использовать replace
вместо set
при установке элементов массива) мне до сих пор кажется странным.
@ boris-petrov Я не думаю, что это связано
привет, мы используем sortablejs для перетаскиваемого списка с помощью ember. Пожалуйста, проверьте эту демонстрацию, чтобы воспроизвести каждую проблему.
шаг:
вы можете видеть, что перетаскиваемый элемент остается в дереве доменов.
но, если перетащить элемент в другую позицию (не в последний элемент), кажется, что это работает хорошо.
Самый полезный комментарий
Думаю, я знаю, что здесь происходит. Это беспорядка, но я постараюсь это описать. Этому способствовало множество крайних случаев (пользовательская ошибка пограничной линии), и я не совсем уверен, что является / не является ошибкой, что и как исправить любую из этих ошибок.
Майор 🔑
Прежде всего, мне нужно описать, что делает параметр
key
в{{#each}}
. TL; DR он пытается определить, когда и имеет ли смысл повторно использовать существующую DOM, а не просто создавать DOM с нуля.Для нашей цели давайте примем как данность, что «прикосновение к DOM» (например, обновление содержимого текстового узла, атрибута, добавление или удаление содержимого и т. Д.) Является дорогостоящим и его следует избегать по мере возможности.
Давайте сосредоточимся на довольно простом шаблоне:
Если
this.names
- это ...Тогда вы получите ...
Все идет нормально.
Добавление элемента в список
А что, если мы добавим в список
{ first: "Andrew", last: "Timberlake" }
? Мы ожидаем, что шаблон создаст следующую модель DOM:Но как_?
Самый наивный способ реализовать помощник
{{#each}}
очищать все содержимое списка каждый раз, когда содержимое списка изменяется. Для этого вам потребуется выполнить не менее 23 операций:<li>
узлов<li>
to-upper-case
4 разаЭто кажется ... очень ненужным и дорогим. Мы _ знаем_, что первые три элемента не изменились, поэтому было бы неплохо, если бы мы могли просто пропустить работу с этими строками.
🔑 @index
Лучшей реализацией было бы попытаться повторно использовать существующие строки и не делать ненужных обновлений. Одна из идей - просто сопоставить строки с их положением в шаблонах. По сути, это то, что делает
key="@index"
:{ first: "Yehuda", last: "Katz" }
с первой строкой<li>Yehuda KATZ</li>
:1.1. "Иегуда" === "Иегуда", нечего делать
1.2. (пространство не содержит динамических данных, поэтому сравнение не требуется)
1.3. «Katz» === «Katz», поскольку помощники «чистые», мы знаем, что нам не придется повторно вызывать помощник
to-upper-case
, и поэтому нам известен вывод этого помощника («KATZ» ) _тоже_ не изменилось, так что делать здесь нечего3.1. Вставить узел
<li>
3.2. Вставить текстовый узел («Андрей»)
3.3. Вставить текстовый узел (пробел)
3.4. Вызов помощника
to-upper-case
("Тимберлейк" -> "ТИМБЕРЛЕЙК")3.5. Вставить текстовый узел ("ТИМБЕРЛЕЙК")
Таким образом, с помощью этой реализации мы сократили общее количество операций с 23 до 5 (размахивая руками над стоимостью сравнений, но для нашей цели мы предполагаем, что они относительно дешевы по сравнению с остальными). Неплохо.
Добавление элемента в список
Но теперь, что произойдет, если вместо _appnding_
{ first: "Andrew", last: "Timberlake" }
в список мы _prepended_ вместо этого? Мы ожидаем, что шаблон создаст следующую модель DOM:Но как_?
{ first: "Andrew", last: "Timberlake" }
с первой строкой<li>Yehuda KATZ</li>
:1.1. «Эндрю»! == «Иегуда», обновите текстовый узел
1.2. (пространство не содержит динамических данных, поэтому сравнение не требуется)
1.3. "Тимберлейк"! == "Кац", повторно вызовите помощника
to-upper-case
1.4. Обновите текстовый узел с "KATZ" на "TIMBERLAKE".
{ first: "Yehuda", last: "Katz" }
со второй строкой,<li>Tom DALE</li>
, еще 3 операции{ first: "Tom", last: "Dale" }
со второй строкой,<li>Godfrey CHAN</li>
, еще 3 операции3.1. Вставить узел
<li>
3.2. Вставить текстовый узел («Годфри»)
3.3. Вставить текстовый узел (пробел)
3.4. Вызов помощника
to-upper-case
("Чан" -> "ЧАН")3.5. Вставить текстовый узел («ЧАН»)
Это 14 операций. Ой!
🔑 @identity
Это казалось ненужным, потому что концептуально, независимо от того, добавляем мы или добавляем, мы все еще изменяем (вставляем) только один объект в массив. Оптимально, мы должны иметь возможность обрабатывать этот случай так же хорошо, как и в сценарии добавления.
Здесь на помощь приходит
key="@identity"
. Вместо того, чтобы полагаться на _order_ элементов в массиве, мы используем их идентификатор объекта JavaScript (===
):===
) первому объекту{ first: "Andrew", last: "Timberlake" }
. Поскольку ничего не найдено, вставьте (добавьте) новую строку:1.1. Вставить узел
<li>
1.2. Вставить текстовый узел («Андрей»)
1.3. Вставить текстовый узел (пробел)
1.4. Вызов помощника
to-upper-case
("Тимберлейк" -> "ТИМБЕРЛЕЙК")1.5. Вставить текстовый узел ("ТИМБЕРЛЕЙК")
===
) второму объекту{ first: "Yehuda", last: "Katz" }
. Найдено<li>Yehuda KATZ</li>
:2.1. "Иегуда" === "Иегуда", нечего делать
2.2. (пространство не содержит динамических данных, поэтому сравнение не требуется)
2.3. «Katz» === «Katz», поскольку помощники «чистые», мы знаем, что нам не придется повторно вызывать помощник
to-upper-case
, и поэтому нам известен вывод этого помощника («KATZ» ) _тоже_ не изменилось, так что делать здесь нечегоНа этом мы вернулись к оптимальным 5 операциям.
Увеличение масштаба
Опять же, это размахивая рукой над сравнениями и бухгалтерскими расходами. В самом деле, они тоже не бесплатны, и в этом очень простом примере они могут не того стоить. Но представьте, что список большой, и каждая строка вызывает сложный компонент (с множеством помощников, вычисляемых свойств, подкомпонентов и т. Д.). Представьте, например, новостную ленту LinkedIn. Если мы не сопоставим правильные строки с правильными данными, аргументы ваших компонентов могут потенциально сильно сбиваться и вызывать гораздо больше обновлений DOM, чем вы могли бы ожидать. Также существуют проблемы с сопоставлением неправильных элементов DOM и потерей состояния DOM, такого как положение курсора и состояние выделения текста.
В целом, в реальном приложении затраты на дополнительное сравнение и бухгалтерский учет в большинстве случаев окупаются. Поскольку
key="@identity"
является значением по умолчанию в Ember и хорошо работает почти во всех случаях, вам обычно не придется беспокоиться об установке аргументаkey
при использовании{{#each}}
.Столкновения 💥
Но подождите, есть проблема. Что насчет этого дела?
Проблема здесь в том, что один и тот же объект _ мог_ появляться несколько раз в одном списке. Это нарушает наш наивный алгоритм
@identity
, в частности ту часть, где мы сказали: «Найдите существующую строку, данные которой совпадают (===
) ...» - это работает только в том случае, если отношение данных к DOM равно 1 : 1, что в данном случае неверно. На практике это может показаться маловероятным, но мы должны с этим справиться.Чтобы избежать этого, мы используем своего рода гибридный подход для обработки этих коллизий. Внутренне сопоставление ключей к DOM выглядит примерно так:
По большей части это встречается довольно редко, а когда все же возникает, большую часть времени это работает достаточно хорошо ™. Если по какой-то причине это не сработает, вы всегда можете использовать путь ключа (или даже более продвинутый механизм ввода ключей в RFC 321 ).
Вернуться к "🐛"
После всего этого разговора мы готовы взглянуть на сценарий в Twiddle.
По сути, мы начали с этого списка:
[undefined, undefined, undefined, undefined, undefined]
.Поскольку мы не указали ключ, Ember по умолчанию использует
@identity
. Далее, поскольку это столкновения, мы получили примерно следующее:Теперь, допустим, мы установили первый флажок:
{{action}}
и повторно отправляется методуmakeChange
[true, undefined, undefined, undefined, undefined]
.Как обновляется DOM?
===
) первому объектуtrue
. Поскольку ничего не найдено, вставьте (добавьте) новую строку<input checked=true ...>0: true...
===
) второму объектуundefined
. Найдено<input ...>0: ...
(ранее ПЕРВАЯ строка):2.1. Обновите текстовый узел
{{idx}}
до1
2.2. В остальном, насколько может судить Эмбер, в этом ряду больше ничего не изменилось, больше нечего делать.
===
) третьему объектуundefined
. Поскольку это второй раз, когда мы видимundefined
, внутренний ключ -undefined-1
, поэтому мы нашли<input ...>1: ...
(ранее ВТОРАЯ строка):3.1. Обновите текстовый узел
{{idx}}
до2
3.2. В остальном, насколько может судить Эмбер, в этом ряду больше ничего не изменилось, больше нечего делать.
undefined-2
иundefined-3
undefined-4
(поскольку после обновления в массиве на однуundefined
меньше)Это объясняет, как мы получили результат, который вы получили в твиддле. По сути, все строки DOM смещены вниз на одну, и новая была вставлена вверху, а
{{idx}}
обновляется для остальных.Действительно неожиданная часть - 2.2. Несмотря на то, что первый флажок (тот, который был нажат) был сдвинут на одну строку вниз во вторую позицию, вы, вероятно, ожидали, что Ember, его свойство
checked
изменилось наtrue
, и поскольку его связанное значение не определено, вы можете ожидать, что Ember вернет его обратно наfalse
, сняв таким образом отметку.Но это не так. Как упоминалось в начале, доступ к DOM стоит дорого. Это включает _reading_ из DOM. Если бы при каждом обновлении нам приходилось считывать последнее значение из DOM для наших сравнений, это в значительной степени нарушило бы цель наших оптимизаций. Поэтому, чтобы этого избежать, мы запомнили последнее значение, которое мы записали в DOM, и сравнили текущее значение с кэшированным значением, не считывая его обратно из DOM. Только когда есть разница, мы записываем новое значение в DOM (и кэшируем его в следующий раз). В этом смысле мы как бы разделяем тот же подход «виртуального DOM», но мы делаем это только на конечных узлах, не виртуализируя «древовидность» всего DOM.
Итак, TL; DR, «привязка» свойства
checked
(или свойстваvalue
текстового поля и т. Д.) На самом деле не работает так, как вы ожидаете. Представьте, что вы визуализировали<div>{{this.name}}</div>
и вручную обновилиtextContent
элементаdiv
используяjQuery
или с помощью инспектора Chrome. Вы не ожидали, что Ember заметит это и обновит за васthis.name
. По сути, это то же самое: поскольку обновление свойстваchecked
произошло за пределами Ember (через поведение браузера по умолчанию для флажка), Ember не узнает об этом.Вот почему существует помощник
{{input}}
. Он должен зарегистрировать соответствующие прослушиватели событий в базовом элементе HTML и отразить операции в соответствующем изменении свойства, чтобы заинтересованные стороны (например, уровень визуализации) могли быть уведомлены.Я не уверен, к чему это нас приведет. Я понимаю, почему это удивительно, но я склонен сказать, что это серия досадных ошибок пользователя. Возможно, нам следует избегать привязки этих свойств к элементам ввода?