Пожалуйста, относитесь к этому вопросу как к чисто образовательному. Мне все еще интересно услышать новые ответы и идеи для реализации этого
ТЛ; др
Как мне реализовать двунаправленное связывание данных с JavaScript?
Привязка данных к DOM
Под привязкой данных к DOM я имею в виду, например, наличие объекта JavaScript a
со свойством b
. Затем наличие <input>
элемента DOM (например), когда элемент DOM изменяется, a
изменяется и наоборот (то есть я имею в виду двунаправленную привязку данных).
Вот диаграмма AngularJS о том, как это выглядит:
Так что в основном у меня есть JavaScript, похожий на:
var a = {b:3};
Затем элемент ввода (или другой формы), такой как:
<input type='text' value=''>
Я хотел бы, чтобы значение ввода было a.b
значением (например), и когда изменяется текст ввода, я бы тоже хотел a.b
изменить. Когда a.b
изменения в JavaScript, входные данные изменяются.
Вопрос
Каковы некоторые основные методы для достижения этой цели в простом JavaScript?
В частности, я хотел бы получить хороший ответ, чтобы сослаться на:
- Как будет работать привязка для объектов?
- Как может работать прослушивание изменений в форме?
- Можно ли простым способом изменить только HTML на уровне шаблона? Я хотел бы не отслеживать привязку в самом HTML-документе, а только в JavaScript (с событиями DOM и JavaScript, сохраняющими ссылку на используемые элементы DOM).
Что я пробовал?
Я большой поклонник усов, поэтому я попытался использовать его для шаблонов. Тем не менее, я столкнулся с проблемами при попытке выполнить привязку данных сам, так как Mustache обрабатывает HTML как строку, поэтому после получения результата у меня нет ссылки на то, где находятся объекты в моей модели представления. Единственный обходной путь, о котором я мог подумать, - это изменение самой строки HTML (или создание дерева DOM) с помощью атрибутов. Я не против использовать другой шаблонизатор.
По сути, у меня возникло сильное ощущение, что я усложняю проблему, и есть простое решение.
Примечание. Пожалуйста, не предоставляйте ответы, которые используют внешние библиотеки, особенно те, которые состоят из тысяч строк кода. Я использовал (и мне нравится!) AngularJS и KnockoutJS. Я действительно не хочу ответов в форме «использовать рамки х». Оптимально, я бы хотел, чтобы будущий читатель не знал, как использовать многие фреймворки, чтобы понять, как реализовать двунаправленную привязку данных. Я не ожидаю полного ответа, но тот, который передает идею.
источник
Ответы:
Абстракция, которая обновляет оба объекта
Я предполагаю, что есть и другие методы, но в конечном итоге у меня будет объект, который содержит ссылку на связанный элемент DOM и предоставляет интерфейс, который координирует обновления своих собственных данных и соответствующего элемента.
.addEventListener()
Обеспечивает очень хороший интерфейс для этого. Вы можете дать ему объект, который реализуетeventListener
интерфейс, и он вызовет свои обработчики с этим объектом в качествеthis
значения.Это дает вам автоматический доступ как к элементу, так и к связанным с ним данным.
Определение вашего объекта
Наследование прототипов - хороший способ реализовать это, хотя, конечно, не обязательно. Сначала вы должны создать конструктор, который получает ваш элемент и некоторые исходные данные.
Поэтому здесь конструктор хранит элемент и данные о свойствах нового объекта. Это также связывает
change
событие с даннымelement
. Интересно то, что он передает новый объект вместо функции в качестве второго аргумента. Но это само по себе не сработает.Реализация
eventListener
интерфейсаЧтобы это работало, ваш объект должен реализовать
eventListener
интерфейс. Все, что нужно для этого - дать объектуhandleEvent()
метод.Вот где приходит наследство.
Есть много разных способов, которыми это можно структурировать, но для вашего примера координации обновлений я решил, что
change()
метод должен принимать только значение иhandleEvent
передавать его вместо объекта события. Таким образом,change()
можно вызывать и без события.Так что теперь, когда
change
событие происходит, оно обновит и элемент, и.data
свойство. И то же самое произойдет, когда вы вызовете.change()
в своей программе JavaScript.Используя код
Теперь вы просто создадите новый объект и позволите ему выполнять обновления. Обновления в коде JS появятся на входе, а события изменения на входе будут видны для кода JS.
ДЕМО: http://jsfiddle.net/RkTMD/
источник
Mustache.render(template,object)
, предполагая, что я хочу синхронизировать объект с шаблоном (не специфичным для Mustache), как бы я продолжил это?input
обновит одну из этих точек, будет сопоставление от входа до этой точки. Я посмотрю, смогу ли я привести быстрый пример.Template
конструктор, который выполняет синтаксический анализ, содержит различныеMyCtor
объекты и предоставляет интерфейс для обновления каждого по его идентификатору. Дайте мне знать, если у вас есть вопросы. :) РЕДАКТИРОВАТЬ: ... используйте эту ссылку вместо этого ... Я забыл, что у меня экспоненциальное увеличение входного значения каждые 10 секунд, чтобы продемонстрировать обновления JS. Это ограничивает это.Итак, я решил бросить свое собственное решение в банк. Вот рабочая скрипка . Обратите внимание, что это работает только в очень современных браузерах.
Что он использует
Эта реализация очень современна - для этого требуется (очень) современный браузер и пользователи двух новых технологий:
MutationObserver
s для обнаружения изменений в домене (также используются слушатели событий)Object.observe
обнаруживать изменения в объекте и уведомлять дом. Опасность, так как этот ответ был написан. Oo обсуждался и решался ECMAScript TC, рассмотрите возможность заполнения .Как это устроено
domAttribute:objAttribute
отображение - напримерbind='textContent:name'
Решение
Вот
dataBind
функция, обратите внимание, что это всего лишь 20 строк кода и может быть короче:Вот некоторое использование:
HTML:
JavaScript:
Вот рабочая скрипка . Обратите внимание, что это решение довольно общее. Object.observe и мутации наблюдателя шимминга доступны.
источник
obj.name
есть сеттер, его нельзя наблюдать извне, но он должен сообщить, что он изменился из сеттера - html5rocks.com/en/tutorials/es7/observe/#toc-notifications - своего рода бросает гаечный ключ в работах для Oo (), если вы хотите более сложное, взаимозависимое поведение с использованием сеттеров. Кроме того, когдаobj.name
это не настраивается, переопределение его установщика (с различными приемами для добавления уведомлений) также не допускается - поэтому дженерики с Oo () полностью отменяются в этом конкретном случае.Я хотел бы добавить к своему составителю. Я предлагаю немного другой подход, который позволит вам просто присвоить новое значение вашему объекту без использования метода. Однако следует отметить, что это не поддерживается особенно старыми браузерами, и IE9 все еще требует использования другого интерфейса.
В частности, мой подход не использует события.
Добытчики и сеттеры
Мое предложение использует сравнительно молодую функцию геттеров и сеттеров , особенно только сеттеров. Вообще говоря, мутаторы позволяют нам «настраивать» поведение того, как определенным свойствам присваивается значение и производится их извлечение.
Одна из реализаций, которую я буду использовать здесь - это метод Object.defineProperty . Это работает в FireFox, GoogleChrome и - я думаю - IE9. Не проверял другие браузеры, но так как это только теория ...
В любом случае, он принимает три параметра. Первый параметр - это объект, для которого вы хотите определить новое свойство, второй - строка, напоминающая имя нового свойства, а последний - «объект дескриптора», предоставляющий информацию о поведении нового свойства.
Два особенно интересных дескриптора есть
get
иset
. Пример будет выглядеть примерно так: Обратите внимание, что использование этих двух запрещает использование других 4 дескрипторов.Теперь использование этого становится немного другим:
Я хочу подчеркнуть, что это работает только для современных браузеров.
Рабочая скрипка: http://jsfiddle.net/Derija93/RkTMD/1/
источник
Proxy
объекты Harmony :) Сеттеры кажутся хорошей идеей, но разве это не потребует от нас модификации реальных объектов? Кроме того, на заметку -Object.create
здесь можно использовать (опять же, если принять во внимание современный браузер, который допускает второй параметр). Кроме того, setter / getter может использоваться для «проецирования» другого значения на объект и элемент DOM :). Я задаюсь вопросом, есть ли у вас какие-либо идеи по поводу шаблонов, это кажется настоящим испытанием, особенно для хорошей структуры :)Proxy
, как вы сказали.;) Я понял, что задача состоит в том, чтобы синхронизировать два разных свойства. Мой метод устраняет одно из обоих.Proxy
избавит от необходимости использовать геттеры / сеттеры, вы можете связывать элементы, не зная, какими свойствами они обладают. Я имел в виду, что геттеры могут изменить больше, чем bindTo.value, они могут содержать логику (и, возможно, даже шаблон). Вопрос в том, как поддерживать двустороннюю привязку такого типа с учетом шаблона? Допустим, я сопоставляю свой объект с формой, я хотел бы сохранить синхронизацию как элемента, так и формы, и мне интересно, как бы я поступил с такими вещами. Вы можете проверить, как это работает на нокауте learn.knockoutjs.com/#/?tutorial=intro, напримерЯ думаю, что мой ответ будет более техническим, но не отличаться, так как другие представляют то же самое, используя разные методы.
Итак, обо всем по порядку, решение этой проблемы - использование шаблона проектирования, известного как «наблюдатель», он позволяет вам отделить ваши данные от презентации, сделав так, чтобы изменения в одной вещи передавались их слушателям, но в этом случае это сделано в двух направлениях.
Для DOM в JS путь
Чтобы привязать данные из DOM к объекту js, вы можете добавить разметку в форме
data
атрибутов (или классов, если вам нужна совместимость), например:Таким образом, к нему можно получить доступ через js используя
querySelectorAll
(или старого другаgetElementsByClassName
для совместимости).Теперь вы можете привязать событие, слушая изменения, следующими способами: один слушатель на объект или один большой слушатель к контейнеру / документу. Привязка к документу / контейнеру вызовет событие для каждого изменения, внесенного в него или его дочерний элемент, уменьшит объем памяти, но вызовет вызовы событий.
Код будет выглядеть примерно так:
Для JS сделать DOM способ
Вам понадобятся две вещи: один мета-объект, который будет содержать ссылки на элемент ведьмы DOM, привязан к каждому объекту / атрибуту js и способ прослушивания изменений в объектах. Это в основном то же самое: вы должны иметь возможность прослушивать изменения в объекте и затем связывать его с узлом DOM, так как ваш объект «не может иметь» метаданные, вам понадобится другой объект, содержащий метаданные таким образом, что имя свойства отображается на свойства объекта метаданных. Код будет примерно таким:
Я надеюсь, что я помог.
источник
Object.observe
поскольку поддержка пока доступна только в Chrome. caniuse.com/#feat=object-observeВчера я начал писать свой собственный способ привязки данных.
С ним очень весело играть.
Я думаю, что это красиво и очень полезно. По крайней мере, в моих тестах, использующих Firefox и Chrome, Edge тоже должен работать. Не уверен насчет других, но если они поддерживают Прокси, думаю, это сработает.
https://jsfiddle.net/2ozoovne/1/
Вот код:
Затем, чтобы установить, просто:
Сейчас я только что добавил привязку значения HTMLInputElement.
Дайте мне знать, если вы знаете, как его улучшить.
источник
В этой ссылке «Простое двухстороннее связывание данных в JavaScript» очень простая базовая реализация двухсторонней привязки данных.
Предыдущая ссылка вместе с идеями из knockoutjs, backbone.js и agility.js привела к этой легкой и быстрой инфраструктуре MVVM, ModelView.js
основанный на JQueryкоторый хорошо сочетается с jQuery и из которого я скромный (или, возможно, не такой скромный) автор.Воспроизведение приведенного ниже примера кода (по ссылке в блоге ):
Пример кода для DataBinder
источник
Изменение значения элемента может вызвать событие DOM . Слушатели, которые отвечают на события, могут использоваться для реализации привязки данных в JavaScript.
Например:
Вот код и демонстрация, которая показывает, как элементы DOM могут быть связаны друг с другом или с объектом JavaScript.
источник
Привязать любой ввод HTML
определить две функции:
использовать функции:
источник
Вот идея использования,
Object.defineProperty
которая напрямую изменяет способ доступа к свойству.Код:
Использование:
скрипка: здесь
источник
Я рассмотрел базовый пример javascript с использованием обработчиков событий onkeypress и onchange для привязки представления к нашим js и js для просмотра
Вот пример плункера http://plnkr.co/edit/7hSOIFRTvqLAvdZT4Bcc?p=preview
источник
источник
Простой способ привязки переменной к входу (двусторонняя привязка) - просто получить прямой доступ к элементу ввода в методах получения и установки:
В HTML:
И использовать:
Причудливый способ сделать вышеупомянутое без getter / setter:
Использовать:
источник
Это очень простая двусторонняя привязка данных в vanilla javascript ....
источник
Поздно на вечеринке, особенно с учетом того, что я написал 2 libs, связанные месяцы / годы назад, я упомяну их позже, но все равно выглядит актуально для меня. Чтобы сделать его действительно коротким спойлером, технологии на мой выбор:
Proxy
для наблюдения за модельюMutationObserver
для отслеживания изменений DOM (по причинам связывания, а не изменениям значений)addEventListener
обработчиковИМХО, в дополнение к ФП, важно, чтобы реализация привязки данных:
user.address.block
shift
,splice
и тому подобное)Учитывая все это, по моему мнению, невозможно просто выбросить несколько десятков линий JS. Я пытался сделать это как шаблон, а не как lib - у меня не получилось.
Затем, после
Object.observe
удаления, и, тем не менее, учитывая, что наблюдение за моделью является критической частью - вся эта часть ДОЛЖНА быть разделена по интересам для другой библиотеки. Теперь о принципах того, как я решил эту проблему - именно так, как ОП спросил:Модель (часть JS)
Для наблюдения за моделями я беру прокси , это единственный разумный способ заставить его работать, ИМХО. Полнофункциональный
observer
заслуживает своей собственной библиотеки, поэтому я разработалobject-observer
библиотеку для этой единственной цели.Модели должны регистрироваться через какой-то специальный API, и именно здесь POJO превращаются в
Observable
s, здесь не видно ярлыков. Элементы DOM, которые считаются связанными представлениями (см. Ниже), обновляются сначала значениями модели, а затем при каждом изменении данных.Представления (часть HTML)
ИМХО, самый чистый способ выразить привязку, это через атрибуты. Многие делали это раньше, а многие будут делать после, так что никаких новостей здесь, это просто правильный способ сделать это. В моем случае я использовал следующий синтаксис:,
<span data-tie="modelKey:path.to.data => targerProperty"></span>
но это менее важно. Что это важно для меня, не сложный синтаксис сценариев в HTML - это не так, опять же , ИМХО.Все элементы, обозначенные как связанные виды, должны быть собраны вначале. С точки зрения производительности, мне кажется неизбежным управлять каким-то внутренним отображением между моделями и представлениями, кажется правильным случаем, когда память + некоторое управление должны быть принесены в жертву для сохранения поисков и обновлений во время выполнения.
Представления обновляются сначала из модели, если она доступна, и при последующих изменениях модели, как мы уже говорили. Более того, весь DOM следует наблюдать с помощью
MutationObserver
, чтобы реагировать (связывать / отменять) на динамически добавляемые / удаляемые / измененные элементы. Кроме того, все это должно быть скопировано в ShadowDOM (конечно, открытый), чтобы не оставлять несвязанных черных дыр.Список спецификаций действительно может пойти дальше, но это, на мой взгляд, основные принципы, которые позволили бы реализовать привязку данных с хорошим балансом полноты функций с одной и вразумительной простотой с другой стороны.
И поэтому, в дополнение к
object-observer
упомянутому выше, я написал такжеdata-tier
библиотеку, которая реализует привязку данных в соответствии с вышеупомянутыми концепциями.источник
За последние 7 лет многое изменилось, сейчас у нас есть встроенные веб-компоненты в большинстве браузеров. IMO Суть проблемы заключается в разделении состояния между элементами, если у вас есть тривиальное обновление пользовательского интерфейса при изменении состояния и наоборот.
Для обмена данными между элементами вы можете создать класс StateObserver и расширить свои веб-компоненты. Минимальная реализация выглядит примерно так:
возиться здесь
Мне нравится этот подход, потому что:
data-
свойстваисточник