См. эту ручку для демонстрации проблемы (на основе слайд-шоу из руководства). Нажав на стрелки «Далее» и «Предыдущий», вы заметите, что усы imgIndex
обновляются правильно, но усы выражения, такие как <p>{{ curImageCaption() }}</p>
, не распознают, когда их значения изменяются.
То есть объект видоизменен таким образом, что значение усов изменится, если выражения будут переоценены, но ractive, похоже, этого не понимает. Есть ли способ заставить это работать, кроме написания адаптеров? Я неправильно понимаю, как работает магический режим? Интересно то, что даже если я явно вызываю ractive.update()
внутри обработчиков событий, ractive все равно не отвечает.
ОБНОВЛЕНИЕ С НОВОЙ ИНФОРМАЦИЕЙ
Немного повозившись, я придумал этот хак, который заставляет его работать. Хитрость состоит в том, чтобы изменить, например, <p>{{ curImageCaption() }}</p>
на <p>{{ curImageCaption(imgIndex) }}</p>
-- добавив к выражению усов простой примитив, который ractive понимает, как правильно смотреть.
Мне кажется, я понимаю, что происходит сейчас, но необходимость явного добавления аргументов в выражение усов, содержащее изменяющиеся примитивы, сводит на нет большую часть цели наличия отдельного объекта предметной области, то есть теперь вы кодируете свой объект предметной области, имея в виду ractive, использование изменяющихся примитивов — своего рода базовый механизм публикации/подписки для уведомления об изменениях.
Было бы неплохо создать настоящий механизм pub/sub для моих пользовательских объектов, на который ractive затем явно подписывается. Проблема в том, как я отметил в OP, даже когда ractive уведомляется об изменении через ractive.update()
, он все еще не знает, что должен пересчитать усы, если только я не использую хак поддельного аргумента. Поэтому неясно, какой callback ractive должен регистрироваться, чтобы все работало.
Я недостаточно хорошо понимаю внутреннюю работу ractive, чтобы сделать это, но я подозреваю, что нужна возможность напрямую работать с _deps
материалом и вручную запускать пересчеты для выражений. Если это звучит правильно, пример того, как это сделать, будет оценен по достоинству.
ОБНОВЛЕНИЕ 2 - Достойное решение
Вот доказательство концепции не слишком хакерского обходного пути.
Идея состоит в том, чтобы использовать свойства ECMA5 для украшения вашего объекта пользовательского домена, предоставляя свойства, которые делегируют существующие методы, которые вы хотите использовать, но которые не работают внутри шаблонов ractive. Свойства, ооо, работают просто отлично.
Таким образом, вместо <p>{{ curImageCaption() }}</p>
мы просто пишем <p>{{ imageCaption }}</p>
, а затем украшаем наш объект пользовательского домена следующим образом:
Object.defineProperty(mySlideshow, "imageCaption", {
configurable: true,
get: function() { return this.curImageCaption() },
set: function() { }
});
Это украшение, немного многословное в моей демонстрации, можно легко уменьшить, создав вспомогательный метод, который принимает объект, сопоставляющий ваши новые имена свойств, удобные для ractive, с именами существующих методов вашего объекта, и заботится о приведенном выше шаблоне для вас. .
ПРИМЕЧАНИЕ. Одним из недостатков этого метода является то, что вам придется вызывать ractive.update()
вручную в обработчиках событий. Я хотел бы знать, есть ли способ обойти это. И если нет, то насколько сильно это повлияет на производительность? Сводит ли это на нет всю цель хирургических обновлений Ractive?
Обновление 3. Лучшее достойное решение?
Эта ручка использует еще один подход, в котором наша модель пользовательского домена связывается с ractive через общий объект диспетчера. (объект, реализующий notify()
). Я думаю, что это мой любимый подход до сих пор....
Это похоже на официальные ractive-адаптеры, но мы используем DI для передачи нашего неофициального ractive-адаптера. в наш объект домена, а не обертывать наш объект. На первый взгляд может показаться, что мы «кодируем на ractive», но на самом деле это верно лишь отчасти. Даже если бы мы использовали другой фреймворк, нам нужно было бы использовать какой-то механизм уведомления для передачи изменений в нашу модель представления, чтобы представления могли на это реагировать. Этот подход DI, кажется, требует меньше шаблонного кода, чем официальные адаптеры ractive, хотя я не понимаю их достаточно хорошо, чтобы знать это наверняка. Это не такое универсальное решение, как официальные адаптеры.
Код с пера для потомков
HTML
<div id='output'></div>
<script id='template' type='text/ractive'>
<div class='slideshow'>
<div class='main'>
<a class='prev' on-tap='prev'><span>«</span></a>
<div class='main-image' style='background-image: url({{ curImageSrc() }});'></div>
<a class='next' on-tap='next'><span>»</span></a>
</div>
<div class='caption'>
<p>{{ curImageCaption() }}</p>
<p>Image index: {{ imgIndex }} </p>
</div>
</div>
</script>
JS
// Fix JS modular arithmetic to always return positive numbers
function mod(m, n) { return ((m%n)+n)%n; }
function SlideshowViewModel(imageData) {
var self = this;
self.imgIndex = 0;
self.next = function() { self.setLegalIndex(self.imgIndex+1); }
self.prev = function() { self.setLegalIndex(self.imgIndex-1); }
self.curImage = function() { return imageData[self.imgIndex]; }
self.curImageSrc = function() { return self.curImage().src; }
self.curImageCaption = function() { return self.curImage().caption; }
self.setLegalIndex = function(newIndex) { self.imgIndex = mod(newIndex, imageData.length); }
}
var mySlideshow = new SlideshowViewModel(
[
{ src: imgPath('problem.gif'), caption: 'Trying to work out a problem after the 5th hour' },
{ src: imgPath('css.gif'), caption: 'Trying to fix someone else\'s CSS' },
{ src: imgPath('ie.gif'), caption: 'Testing interface on Internet Explorer' },
{ src: imgPath('w3c.gif'), caption: 'Trying to code to W3C standards' },
{ src: imgPath('build.gif'), caption: 'Visiting the guy that wrote the build scripts' },
{ src: imgPath('test.gif'), caption: 'I don\'t need to test that. What can possibly go wrong?' }
]
);
var ractive = new Ractive({
el: '#output',
template: '#template',
data: mySlideshow,
magic: true
});
ractive.on( 'next', function(event) {
ractive.data.next();
});
ractive.on( 'prev', function(event) {
ractive.data.prev();
});
function imgPath(name) { return 'http://learn.ractivejs.org/files/gifs/' + name; }