请参阅此笔以获取问题的演示(基于教程中的幻灯片)。当单击“下一步”和“上一个”箭头时,您会注意到imgIndex
mustache 正确更新,但表达式 mustache 例如<p>{{ curImageCaption() }}</p>
不识别它们的值何时发生变化。
也就是说,对象发生了变异,因此如果重新评估表达式,胡子值会发生变化,但 ractive 似乎没有意识到这一点。有没有办法让它工作,除非写适配器?我是否误解了魔术模式的工作原理?有趣的是,即使我ractive.update()
在事件处理程序内部显式调用,ractive 仍然没有响应。
更新新信息
经过更多的摆弄,我想出了这个让它工作的技巧。hack 是要改变,例如,<p>{{ curImageCaption() }}</p>
向<p>{{ curImageCaption(imgIndex) }}</p>
mustache 表达式添加一个简单的原语,以便 ractive 了解如何正确观看。
我想我知道现在发生了什么,但是必须显式地向包含更改原语的 mustache 表达式添加参数,这违背了拥有单独域对象的大部分目的——也就是说,现在你在编码你的域对象时考虑到了 ractive,使用更改原语是一种基本的发布/订阅机制,用于通知 ractive 更改。
必须在我的自定义对象上创建一个真正的 pub/sub 机制,然后 ractive 然后显式订阅,这很好。问题是,正如我在 OP 中指出的那样,即使通过 ractive 收到更改通知ractive.update()
,它仍然不知道它应该重新计算胡须,除非我使用假参数 hack。因此,尚不清楚应该注册什么回调 ractive 以使一切正常。
我不太了解 ractive 的内部工作来做到这一点,但我怀疑需要的是直接使用这些_deps
东西的能力,并手动触发表达式的重新计算。如果这听起来正确,将不胜感激提供如何实现它的示例。
更新 2——一个不错的解决方案
这是一个不太老套的解决方法的概念证明。
这个想法是使用 ECMA5 属性来装饰您的自定义域对象,提供委托给您想要使用但在 ractive 模板中不起作用的现有方法的属性。otoh的属性工作得很好。
因此,我们不再<p>{{ curImageCaption() }}</p>
简单地编写<p>{{ imageCaption }}</p>
,然后像这样装饰我们的自定义域对象:
Object.defineProperty(mySlideshow, "imageCaption", {
configurable: true,
get: function() { return this.curImageCaption() },
set: function() { }
});
这种装饰,在我的演示中有点冗长,可以通过创建一个辅助方法来轻松精简.
注意:此方法的一个缺点是您必须ractive.update()
在事件处理程序中手动调用。我想知道是否有办法解决这个问题。如果没有,这会对性能造成多大的影响?它是否违背了 ractive 手术更新的全部目的?
更新 3——更好的解决方案?
这支笔采用了另一种方法,其中通过通用调度程序对象(实现的对象notify()
)将我们的自定义域模型与 ractive 链接起来。我认为这是迄今为止我最喜欢的方法....
它类似于官方的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; }